/* -----------------------------------------------------------------------------
 * This file is part of SWIG, which is licensed as a whole under version 3
 * (or any later version) of the GNU General Public License. Some additional
 * terms also apply to certain portions of SWIG. The full details of the SWIG
 * license and copyrights can be found in the LICENSE and COPYRIGHT files
 * included with the SWIG source code as distributed by the SWIG developers
 * and at https://www.swig.org/legal.html.
 *
 * go.cxx
 *
 * Go language module for SWIG.
 * ----------------------------------------------------------------------------- */

#include "swigmod.h"
#include "cparse.h"
#include <ctype.h>

/* ----------------------------------------------------------------------
 * siphash()
 *
 * 64-bit SipHash-2-4 to generate unique id for each module
 * ---------------------------------------------------------------------- */

// An unsigned 64-bit integer that works on a 32-bit host.
typedef struct {
    // Assume unsigned long is at least 32 bits.
    unsigned long hi;
    unsigned long lo;
} swig_uint64;

// Rotate v left by bits, which must be <= 32.
static inline void _rotl(swig_uint64 *v, int bits) {
    assert(bits <= 32);
    unsigned long tmp = v->hi;
    if (bits == 32) {
        v->hi = v->lo;
        v->lo = tmp;
    } else {
        v->hi = (tmp << bits) | ((0xfffffffful & v->lo) >> (32 - bits));
        v->lo = (v->lo << bits) | ((0xfffffffful & tmp) >> (32 - bits));
    }
}

// dst ^= src
static inline void _xor(swig_uint64 *dst, swig_uint64 *src) {
    dst->lo ^= src->lo;
    dst->hi ^= src->hi;
}

// dst += src
static inline void _add(swig_uint64 *dst, swig_uint64 *src) {
    dst->lo += src->lo;
    dst->hi += src->hi + ((dst->lo & 0xfffffffful) < (src->lo & 0xfffffffful) ? 1 : 0);
}

#define SIPROUND                                        \
  do {                                                  \
    _add(&v0, &v1); _rotl(&v1, 13); _xor(&v1, &v0); _rotl(&v0, 32); \
    _add(&v2, &v3); _rotl(&v3, 16); _xor(&v3, &v2); \
    _add(&v0, &v3); _rotl(&v3, 21); _xor(&v3, &v0); \
    _add(&v2, &v1); _rotl(&v1, 17); _xor(&v1, &v2); _rotl(&v2, 32); \
  } while(0)

// Set out to the hash of inc/inlen.
static void siphash(swig_uint64 *out, const char *inc, unsigned long inlen) {
    /* "somepseudorandomlygeneratedbytes" */
    swig_uint64 v0 = {0x736f6d65UL, 0x70736575UL};
    swig_uint64 v1 = {0x646f7261UL, 0x6e646f6dUL};
    swig_uint64 v2 = {0x6c796765UL, 0x6e657261UL};
    swig_uint64 v3 = {0x74656462UL, 0x79746573UL};
    swig_uint64 b;
    /* hard-coded k. */
    swig_uint64 k0 = {0x07060504UL, 0x03020100UL};
    swig_uint64 k1 = {0x0F0E0D0CUL, 0x0B0A0908UL};
    int i;
    const int cROUNDS = 2, dROUNDS = 4;
    const unsigned char *in = (const unsigned char *) inc;
    const unsigned char *end = in + inlen - (inlen % 8);
    int left = inlen & 7;
    _xor(&v3, &k1);
    _xor(&v2, &k0);
    _xor(&v1, &k1);
    _xor(&v0, &k0);
    for (; in != end; in += 8) {
        b.hi = 0;
        b.lo = 0;
        for (i = 0; i < 4; i++) {
            b.lo |= ((unsigned long) in[i]) << (8 * i);
        }
        for (i = 0; i < 4; i++) {
            b.hi |= ((unsigned long) in[i + 4]) << (8 * i);
        }
        _xor(&v3, &b);
        for (i = 0; i < cROUNDS; i++) {
            SIPROUND;
        }
        _xor(&v0, &b);
    }
    b.hi = (inlen & 0xff) << 24;
    b.lo = 0;
    for (; left; left--) {
        if (left > 4) {
            b.hi |= ((unsigned long) in[left - 1]) << (8 * left - 8 - 32);
        } else {
            b.lo |= ((unsigned long) in[left - 1]) << (8 * left - 8);
        }
    }
    _xor(&v3, &b);
    for (i = 0; i < cROUNDS; i++) {
        SIPROUND;
    }
    _xor(&v0, &b);
    v2.lo ^= 0xff;
    for (i = 0; i < dROUNDS; i++) {
        SIPROUND;
    }
    out->lo = 0;
    out->hi = 0;
    _xor(out, &v0);
    _xor(out, &v1);
    _xor(out, &v2);
    _xor(out, &v3);
}

#undef SIPROUND

class GO : public Language {
    static const char *const usage;

    // Go package name.
    String *package;
    // SWIG module name.
    String *module;
    // Flag for generating gccgo output.
    bool gccgo_flag;
    // Prefix to use with gccgo.
    String *go_prefix;
    // -fgo-prefix option.
    String *prefix_option;
    // -fgo-pkgpath option.
    String *pkgpath_option;
    // Prefix for translating %import directive to import statements.
    String *import_prefix;
    // Whether to use a shared library.
    bool use_shlib;
    // Name of shared library to import.
    String *soname;
    // Size in bits of the Go type "int".  0 if not specified.
    int intgo_type_size;

    /* Output files */
    File *f_c_begin;
    File *f_go_begin;

    /* Output fragments */
    File *f_c_runtime;
    File *f_c_header;
    File *f_c_wrappers;
    File *f_c_init;
    File *f_c_directors;
    File *f_c_directors_h;
    File *f_go_imports;
    File *f_go_runtime;
    File *f_go_header;
    File *f_go_wrappers;
    File *f_go_directors;
    File *f_cgo_comment;
    File *f_cgo_comment_typedefs;

    // True if we imported a module.
    bool saw_import;
    // If not NULL, name of import package being processed.
    String *imported_package;
    // Build interface methods while handling a class.  This is only
    // non-NULL when we are handling methods.
    String *interfaces;
    // The class node while handling a class.  This is only non-NULL
    // when we are handling methods.
    Node *class_node;
    // The class name while handling a class.  This is only non-NULL
    // when we are handling methods.  This is the name of the class as
    // SWIG sees it.
    String *class_name;
    // The receiver name while handling a class.  This is only non-NULL
    // when we are handling methods.  This is the name of the class
    // as run through goCPointerType.
    String *class_receiver;
    // A hash table of method names that we have seen when processing a
    // class.  This lets us detect base class methods that we don't want
    // to use.
    Hash *class_methods;
    // True when we are generating the wrapper functions for a variable.
    bool making_variable_wrappers;
    // True when working with a static member function.
    bool is_static_member_function;
    // A hash table of enum types that we have seen but which may not have
    // been defined.  The index is a SwigType.
    Hash *undefined_enum_types;
    // A hash table of types that we have seen but which may not have
    // been defined.  The index is a SwigType.
    Hash *undefined_types;
    // A hash table of classes which were defined.  The index is a Go
    // type name.
    Hash *defined_types;
    // A hash table of all the go_imports already imported. The index is a full
    // import name e.g. '"runtime"' or '_ "runtime/cgo"' or 'sc "syscall"'.
    Hash *go_imports;
    // A unique ID used to make public symbols unique.
    String *unique_id;

public:
    GO() : package(NULL),
           module(NULL),
           gccgo_flag(false),
           go_prefix(NULL),
           prefix_option(NULL),
           pkgpath_option(NULL),
           import_prefix(NULL),
           use_shlib(false),
           soname(NULL),
           intgo_type_size(0),
           f_c_begin(NULL),
           f_go_begin(NULL),
           f_c_runtime(NULL),
           f_c_header(NULL),
           f_c_wrappers(NULL),
           f_c_init(NULL),
           f_c_directors(NULL),
           f_c_directors_h(NULL),
           f_go_imports(NULL),
           f_go_runtime(NULL),
           f_go_header(NULL),
           f_go_wrappers(NULL),
           f_go_directors(NULL),
           f_cgo_comment(NULL),
           f_cgo_comment_typedefs(NULL),
           saw_import(false),
           imported_package(NULL),
           interfaces(NULL),
           class_node(NULL),
           class_name(NULL),
           class_receiver(NULL),
           class_methods(NULL),
           making_variable_wrappers(false),
           is_static_member_function(false),
           undefined_enum_types(NULL),
           undefined_types(NULL),
           defined_types(NULL),
           go_imports(NULL),
           unique_id(NULL) {
        director_multiple_inheritance = 1;
        directorLanguage();
        director_prot_ctor_code = NewString("_swig_gopanic(\"accessing abstract class or protected constructor\");");
    }

private:
    /* ------------------------------------------------------------
   * main()
   * ------------------------------------------------------------ */
    virtual void main(int argc, char *argv[]) {

        SWIG_library_directory("go");
        bool saw_nocgo_flag = false;

        // Process command line options.
        for (int i = 1; i < argc; i++) {
            if (argv[i]) {
                if (strcmp(argv[i], "-package") == 0) {
                    if (argv[i + 1]) {
                        package = NewString(argv[i + 1]);
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        i++;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-cgo") == 0) {
                    Swig_mark_arg(i);
                } else if (strcmp(argv[i], "-no-cgo") == 0) {
                    Swig_mark_arg(i);
                    saw_nocgo_flag = true;
                } else if (strcmp(argv[i], "-gccgo") == 0) {
                    Swig_mark_arg(i);
                    gccgo_flag = true;
                } else if (strcmp(argv[i], "-go-prefix") == 0) {
                    if (argv[i + 1]) {
                        prefix_option = NewString(argv[i + 1]);
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        i++;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-go-pkgpath") == 0) {
                    if (argv[i + 1]) {
                        pkgpath_option = NewString(argv[i + 1]);
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        i++;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-import-prefix") == 0) {
                    if (argv[i + 1]) {
                        import_prefix = NewString(argv[i + 1]);
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        i++;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-use-shlib") == 0) {
                    Swig_mark_arg(i);
                    use_shlib = true;
                } else if (strcmp(argv[i], "-soname") == 0) {
                    if (argv[i + 1]) {
                        soname = NewString(argv[i + 1]);
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        i++;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-longsize") == 0) {
                    // Ignore for backward compatibility.
                    if (argv[i + 1]) {
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        ++i;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-intgosize") == 0) {
                    if (argv[i + 1]) {
                        intgo_type_size = atoi(argv[i + 1]);
                        if (intgo_type_size != 32 && intgo_type_size != 64) {
                            Printf(stderr, "-intgosize not 32 or 64\n");
                            Swig_arg_error();
                        }
                        Swig_mark_arg(i);
                        Swig_mark_arg(i + 1);
                        ++i;
                    } else {
                        Swig_arg_error();
                    }
                } else if (strcmp(argv[i], "-help") == 0) {
                    Printf(stdout, "%s\n", usage);
                }
            }
        }

        if (saw_nocgo_flag) {
            Printf(stderr, "SWIG -go: -no-cgo option is no longer supported\n");
            Exit(EXIT_FAILURE);
        }

        if (gccgo_flag && !pkgpath_option && !prefix_option) {
            prefix_option = NewString("go");
        }

        // Add preprocessor symbol to parser.
        Preprocessor_define("SWIGGO 1", 0);

        if (gccgo_flag) {
            Preprocessor_define("SWIGGO_GCCGO 1", 0);
        }

        if (intgo_type_size == 32) {
            Preprocessor_define("SWIGGO_INTGO_SIZE 32", 0);
        } else if (intgo_type_size == 64) {
            Preprocessor_define("SWIGGO_INTGO_SIZE 64", 0);
        } else {
            Preprocessor_define("SWIGGO_INTGO_SIZE 0", 0);
        }

        // Add typemap definitions.
        SWIG_typemap_lang("go");
        SWIG_config_file("go.swg");

        allow_overloading();
    }

    /* ---------------------------------------------------------------------
   * top()
   *
   * For gc, we are going to create the following files:
   *
   * 1) A .c or .cxx file compiled with gcc.  This file will contain
   *    function wrappers.  Each wrapper will take a pointer to a
   *    struct holding the arguments, unpack them, and call the real
   *    function.
   *
   * 2) A .go file which defines the Go form of all types, and which
   *    defines Go function wrappers.  Each wrapper will call the C
   *    function wrapper in the second file.
   *
   * 3) A .c file compiled with 6c/8c.  This file will define
   *    Go-callable C function wrappers.  Each wrapper will use
   *    cgocall to call the function wrappers in the first file.
   *
   * When generating code for gccgo, we don't need the third file, and
   * the function wrappers in the first file have a different form.
   *
   * --------------------------------------------------------------------- */

    virtual int top(Node *n) {
        Node *optionsnode = Getattr(Getattr(n, "module"), "options");
        if (optionsnode) {
            if (Getattr(optionsnode, "directors")) {
                allow_directors();
            }
            if (Getattr(optionsnode, "dirprot")) {
                allow_dirprot();
            }
            allow_allprotected(GetFlag(optionsnode, "allprotected"));
        }

        module = Getattr(n, "name");
        if (!package) {
            package = Copy(module);
        }
        if (!soname && use_shlib) {
            soname = Copy(package);
            Append(soname, ".so");
        }

        if (gccgo_flag) {
            String *pref;
            if (pkgpath_option) {
                pref = pkgpath_option;
            } else {
                pref = prefix_option;
            }
            go_prefix = NewString("");
            for (char *p = Char(pref); *p != '\0'; p++) {
                if ((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || (*p >= '0' && *p <= '9') || *p == '.' ||
                    *p == '$') {
                    Putc(*p, go_prefix);
                } else {
                    Putc('_', go_prefix);
                }
            }
            if (!pkgpath_option) {
                Append(go_prefix, ".");
                Append(go_prefix, getModuleName(package));
            }
        }

        // Get filenames.

        String *swig_filename = Getattr(n, "infile");
        String *c_filename = Getattr(n, "outfile");
        String *c_filename_h = Getattr(n, "outfile_h");

        String *go_filename = NewString("");
        Printf(go_filename, "%s%s.go", SWIG_output_directory(), module);

        // Generate a unique ID based on a hash of the SWIG input.
        swig_uint64 hash = {0, 0};
        FILE *swig_input = Swig_open(swig_filename);
        if (swig_input == NULL) {
            FileErrorDisplay(swig_filename);
            Exit(EXIT_FAILURE);
        }
        String *swig_input_content = Swig_read_file(swig_input);
        siphash(&hash, Char(swig_input_content), Len(swig_input_content));
        Delete(swig_input_content);
        fclose(swig_input);
        unique_id = NewString("");
        Printf(unique_id, "_%s", getModuleName(package));

        // Open files.

        f_c_begin = NewFile(c_filename, "w", SWIG_output_files());
        if (!f_c_begin) {
            FileErrorDisplay(c_filename);
            Exit(EXIT_FAILURE);
        }

        if (Swig_directors_enabled()) {
            if (!c_filename_h) {
                Printf(stderr, "Unable to determine outfile_h\n");
                Exit(EXIT_FAILURE);
            }
            f_c_directors_h = NewFile(c_filename_h, "w", SWIG_output_files());
            if (!f_c_directors_h) {
                FileErrorDisplay(c_filename_h);
                Exit(EXIT_FAILURE);
            }
        }

        f_go_begin = NewFile(go_filename, "w", SWIG_output_files());
        if (!f_go_begin) {
            FileErrorDisplay(go_filename);
            Exit(EXIT_FAILURE);
        }

        f_c_runtime = NewString("");
        f_c_header = NewString("");
        f_c_wrappers = NewString("");
        f_c_init = NewString("");
        f_c_directors = NewString("");
        f_go_imports = NewString("");
        f_go_runtime = NewString("");
        f_go_header = NewString("");
        f_go_wrappers = NewString("");
        f_go_directors = NewString("");
        f_cgo_comment = NewString("");
        f_cgo_comment_typedefs = NewString("");

        Swig_register_filebyname("begin", f_c_begin);
        Swig_register_filebyname("runtime", f_c_runtime);
        Swig_register_filebyname("header", f_c_header);
        Swig_register_filebyname("wrapper", f_c_wrappers);
        Swig_register_filebyname("init", f_c_init);
        Swig_register_filebyname("director", f_c_directors);
        Swig_register_filebyname("director_h", f_c_directors_h);
        Swig_register_filebyname("go_begin", f_go_begin);
        Swig_register_filebyname("go_imports", f_go_imports);
        Swig_register_filebyname("go_runtime", f_go_runtime);
        Swig_register_filebyname("go_header", f_go_header);
        Swig_register_filebyname("go_wrapper", f_go_wrappers);
        Swig_register_filebyname("go_director", f_go_directors);
        Swig_register_filebyname("cgo_comment", f_cgo_comment);
        Swig_register_filebyname("cgo_comment_typedefs", f_cgo_comment_typedefs);

        Swig_banner(f_c_begin);

        Swig_obligatory_macros(f_c_runtime, "GO");

        if (CPlusPlus) {
            Printf(f_c_begin, "\n// source: %s\n\n", swig_filename);
        } else {
            Printf(f_c_begin, "\n/* source: %s */\n\n", swig_filename);
        }

        Printf(f_c_runtime, "#define SWIGMODULE %s\n", module);

        if (gccgo_flag) {
            Printf(f_c_runtime, "#define SWIGGO_PREFIX %s\n", go_prefix);
        }

        if (Swig_directors_enabled()) {
            Printf(f_c_runtime, "#define SWIG_DIRECTORS\n");

            Swig_banner(f_c_directors_h);
            Printf(f_c_directors_h, "\n// source: %s\n\n", swig_filename);

            Printf(f_c_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module);
            Printf(f_c_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module);
            Printf(f_c_directors_h, "class Swig_memory;\n\n");

            Printf(f_c_directors, "\n// C++ director class methods.\n");
            String *filename = Swig_file_filename(c_filename_h);
            Printf(f_c_directors, "#include \"%s\"\n\n", filename);
            Delete(filename);
        }

        Swig_banner(f_go_begin);
        Printf(f_go_begin, "\n// source: %s\n", swig_filename);

        Printv(f_cgo_comment_typedefs, "/*\n", NULL);

        // The cgo program defines the intgo type after our function
        // definitions, but we want those definitions to be able to use
        // intgo also.
        Printv(f_cgo_comment_typedefs, "#define intgo swig_intgo\n", NULL);
        Printv(f_cgo_comment_typedefs, "typedef void *swig_voidp;\n", NULL);

        // Output module initialization code.

        Printf(f_go_begin, "\npackage %s\n\n", getModuleName(package));

        // All the C++ wrappers should be extern "C".

        Printv(f_c_wrappers, "#ifdef __cplusplus\n", "extern \"C\" {\n", "#endif\n\n", NULL);

        // Set up the hash table for types not defined by SWIG.

        undefined_enum_types = NewHash();
        undefined_types = NewHash();
        defined_types = NewHash();
        go_imports = NewHash();

        // Emit code.

        Language::top(n);

        if (Swig_directors_enabled()) {
            // Insert director runtime into the f_runtime file (make it occur before %header section)
            Swig_insert_file("director_common.swg", f_c_runtime);
            Swig_insert_file("director.swg", f_c_runtime);
        }

        Delete(go_imports);

        // Write out definitions for the types not defined by SWIG.

        if (Len(undefined_enum_types) > 0)
            Printv(f_go_wrappers, "\n", NULL);
        for (Iterator p = First(undefined_enum_types); p.key; p = Next(p)) {
            String *name = p.item;
            Printv(f_go_wrappers, "type ", name, " int\n", NULL);
        }

        Printv(f_go_wrappers, "\n", NULL);
        for (Iterator p = First(undefined_types); p.key; p = Next(p)) {
            String *ty = goType(NULL, p.key);
            if (!Getattr(defined_types, ty)) {
                String *cp = goCPointerType(p.key, false);
                if (!Getattr(defined_types, cp)) {
                    Printv(f_go_wrappers, "type ", cp, " uintptr\n", NULL);
                    Printv(f_go_wrappers, "type ", ty, " interface {\n", NULL);
                    Printv(f_go_wrappers, "\tSwigcptr() uintptr;\n", NULL);
                    Printv(f_go_wrappers, "}\n", NULL);
                    Printv(f_go_wrappers, "func (p ", cp, ") Swigcptr() uintptr {\n", NULL);
                    Printv(f_go_wrappers, "\treturn uintptr(p)\n", NULL);
                    Printv(f_go_wrappers, "}\n\n", NULL);
                }
                Delete(cp);
            }
            Delete(ty);
        }
        Delete(undefined_enum_types);
        Delete(undefined_types);
        Delete(defined_types);

        /* Write and cleanup */

        Dump(f_c_header, f_c_runtime);

        if (Swig_directors_enabled()) {
            Printf(f_c_directors_h, "#endif\n");
            Delete(f_c_directors_h);
            f_c_directors_h = NULL;

            Dump(f_c_directors, f_c_runtime);
            Delete(f_c_directors);
            f_c_directors = NULL;
        }

        // End the extern "C".
        Printv(f_c_wrappers, "#ifdef __cplusplus\n", "}\n", "#endif\n\n", NULL);

        // End the cgo comment.
        Printv(f_cgo_comment, "#undef intgo\n", NULL);
        Printv(f_cgo_comment, "*/\n", NULL);
        Printv(f_cgo_comment, "import \"C\"\n", NULL);
        Printv(f_cgo_comment, "\n", NULL);

        bool need_panic = false;
        if (Strstr(f_c_runtime, "SWIG_contract_assert(") != 0 || Strstr(f_c_wrappers, "SWIG_contract_assert(") != 0) {
            Printv(f_c_begin, "\n#define SWIG_contract_assert(expr, msg) if (!(expr)) { _swig_gopanic(msg); } else\n\n",
                   NULL);
            need_panic = true;
        }

        if (!gccgo_flag &&
            (need_panic || Strstr(f_c_runtime, "_swig_gopanic") != 0 || Strstr(f_c_wrappers, "_swig_gopanic") != 0)) {
            Printv(f_go_header, "//export cgo_panic_", unique_id, "\n", NULL);
            Printv(f_go_header, "func cgo_panic_", unique_id, "(p *byte) {\n", NULL);
            Printv(f_go_header, "\ts := (*[1024]byte)(unsafe.Pointer(p))[:]\n", NULL);
            Printv(f_go_header, "\tfor i, b := range s {\n", NULL);
            Printv(f_go_header, "\t\tif b == 0 {\n", NULL);
            Printv(f_go_header, "\t\t\tpanic(string(s[:i]))\n", NULL);
            Printv(f_go_header, "\t\t}\n", NULL);
            Printv(f_go_header, "\t}\n", NULL);
            Printv(f_go_header, "\tpanic(string(s))\n", NULL);
            Printv(f_go_header, "}\n\n", NULL);

            Printv(f_c_begin, "\nextern\n", NULL);
            Printv(f_c_begin, "#ifdef __cplusplus\n", NULL);
            Printv(f_c_begin, "  \"C\"\n", NULL);
            Printv(f_c_begin, "#endif\n", NULL);
            Printv(f_c_begin, "  void cgo_panic_", unique_id, "(const char*);\n", NULL);
            Printv(f_c_begin, "static void _swig_gopanic(const char *p) {\n", NULL);
            Printv(f_c_begin, "  cgo_panic_", unique_id, "(p);\n", NULL);
            Printv(f_c_begin, "}\n\n", NULL);
        }

        Dump(f_c_runtime, f_c_begin);
        Dump(f_c_wrappers, f_c_begin);
        Dump(f_c_init, f_c_begin);
        Dump(f_cgo_comment_typedefs, f_go_begin);
        Dump(f_cgo_comment, f_go_begin);
        Dump(f_go_imports, f_go_begin);
        Dump(f_go_header, f_go_begin);
        Dump(f_go_runtime, f_go_begin);
        Dump(f_go_wrappers, f_go_begin);
        if (Swig_directors_enabled()) {
            Dump(f_go_directors, f_go_begin);
        }
        Delete(f_c_runtime);
        Delete(f_c_header);
        Delete(f_c_wrappers);
        Delete(f_c_init);
        Delete(f_go_imports);
        Delete(f_go_runtime);
        Delete(f_go_header);
        Delete(f_go_wrappers);
        Delete(f_go_directors);
        Delete(f_cgo_comment);
        Delete(f_cgo_comment_typedefs);
        Delete(f_c_begin);
        Delete(f_go_begin);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * importDirective()
   *
   * Handle a SWIG import statement by generating a Go import
   * statement.
   * ------------------------------------------------------------ */

    virtual int importDirective(Node *n) {
        String *hold_import = imported_package;
        String *modname = Getattr(n, "module");
        if (modname) {
            if (!Getattr(go_imports, modname)) {
                Setattr(go_imports, modname, modname);
                Printv(f_go_imports, "import \"", NULL);
                if (import_prefix) {
                    Printv(f_go_imports, import_prefix, "/", NULL);
                }
                Printv(f_go_imports, modname, "\"\n", NULL);
            }
            imported_package = modname;
            saw_import = true;
        }
        int r = Language::importDirective(n);
        imported_package = hold_import;
        return r;
    }

    /* ----------------------------------------------------------------------
   * Language::insertDirective()
   *
   * If the section is go_imports, store them for later.
   * ---------------------------------------------------------------------- */
    virtual int insertDirective(Node *n) {
        char *section = Char(Getattr(n, "section"));
        if ((ImportMode && !Getattr(n, "generated")) ||
            !section || (strcmp(section, "go_imports") != 0)) {
            return Language::insertDirective(n);
        }

        char *code = Char(Getattr(n, "code"));
        char *pch = strtok(code, ",");
        while (pch != NULL) {
            // Do not import same thing more than once.
            if (!Getattr(go_imports, pch)) {
                Setattr(go_imports, pch, pch);
                Printv(f_go_imports, "import ", pch, "\n", NULL);
            }
            pch = strtok(NULL, ",");
        }
        return SWIG_OK;
    }

    /* ----------------------------------------------------------------------
   * functionWrapper()
   *
   * Implement a function.
   * ---------------------------------------------------------------------- */

    virtual int functionWrapper(Node *n) {
        if (GetFlag(n, "feature:ignore")) {
            return SWIG_OK;
        }

        // We don't need explicit calls.
        if (GetFlag(n, "explicitcall")) {
            return SWIG_OK;
        }

        String *name = Getattr(n, "sym:name");
        String *nodetype = Getattr(n, "nodeType");
        bool is_static = is_static_member_function || isStatic(n);
        bool is_friend = isFriend(n);
        bool is_ctor_dtor = false;

        SwigType *result = Getattr(n, "type");

        // For some reason SWIG changs the "type" value during the call to
        // functionWrapper.  We need to remember the type for possible
        // overload processing.
        Setattr(n, "go:type", Copy(result));

        String *go_name;

        String *r1 = NULL;
        if (making_variable_wrappers) {
            // Change the name of the variable setter and getter functions
            // to be more Go like.

            bool is_set = Strcmp(Char(name) + Len(name) - 4, "_set") == 0;
            assert(is_set || Strcmp(Char(name) + Len(name) - 4, "_get") == 0);

            // Start with Set or Get.
            go_name = NewString(is_set ? "Set" : "Get");

            // If this is a static variable, put in the class name,
            // capitalized.
            if (is_static && class_name) {
                String *ccn = exportedName(class_name);
                Append(go_name, ccn);
                Delete(ccn);
            }

            // Add the rest of the name, capitalized, dropping the _set or
            // _get.
            String *c1 = removeClassname(name);
            String *c2 = exportedName(c1);
            char *p = Char(c2);
            int len = Len(p);
            for (int i = 0; i < len - 4; ++i) {
                Putc(p[i], go_name);
            }
            Delete(c2);
            Delete(c1);

            if (!checkIgnoredParameters(n, go_name)) {
                Delete(go_name);
                return SWIG_NOWRAP;
            }
        } else if (Cmp(nodetype, "constructor") == 0) {
            is_ctor_dtor = true;

            // Change the name of a constructor to be more Go like.  Change
            // new_ to New, and capitalize the class name.
            assert(Strncmp(name, "new_", 4) == 0);
            String *c1 = NewString(Char(name) + 4);
            String *c2 = exportedName(c1);
            go_name = NewString("New");
            Append(go_name, c2);
            Delete(c2);
            Delete(c1);

            if (Swig_methodclass(n) && Swig_directorclass(n)) {
                // The core SWIG code skips the first parameter when
                // generating the $nondirector_new string.  Recreate the
                // action in this case.  But don't it if we are using the
                // special code for an abstract class.
                String *call = Swig_cppconstructor_call(getClassType(),
                                                        Getattr(n, "parms"));
                SwigType *type = Copy(getClassType());
                SwigType_add_pointer(type);
                String *cres = Swig_cresult(type, Swig_cresult_name(), call);
                if (!Equal(Getattr(n, "wrap:action"), director_prot_ctor_code)) {
                    Setattr(n, "wrap:action", cres);
                }
            }
        } else if (Cmp(nodetype, "destructor") == 0) {
            // No need to emit protected destructors.
            if (!is_public(n)) {
                return SWIG_OK;
            }

            is_ctor_dtor = true;

            // Change the name of a destructor to be more Go like.  Change
            // delete_ to Delete and capitalize the class name.
            assert(Strncmp(name, "delete_", 7) == 0);
            String *c1 = NewString(Char(name) + 7);
            String *c2 = exportedName(c1);
            go_name = NewString("Delete");
            Append(go_name, c2);
            Delete(c2);
            Delete(c1);

            result = NewString("void");
            r1 = result;
        } else {
            if (!checkFunctionVisibility(n, NULL)) {
                return SWIG_OK;
            }

            go_name = buildGoName(name, is_static, is_friend);

            if (!checkIgnoredParameters(n, go_name)) {
                Delete(go_name);
                return SWIG_NOWRAP;
            }
        }

        String *overname = NULL;
        if (Getattr(n, "sym:overloaded")) {
            overname = Getattr(n, "sym:overname");
        } else {
            String *scope;
            if (!class_name || is_static || is_ctor_dtor) {
                scope = NULL;
            } else {
                scope = NewString("swiggoscope.");
                Append(scope, class_name);
            }
            if (!checkNameConflict(go_name, n, scope)) {
                Delete(go_name);
                return SWIG_NOWRAP;
            }
        }

        String *wname = Swig_name_wrapper(name);
        if (overname) {
            Append(wname, overname);
        }
        Append(wname, unique_id);
        Setattr(n, "wrap:name", wname);

        ParmList *parms = Getattr(n, "parms");
        Setattr(n, "wrap:parms", parms);

        int r = makeWrappers(n, go_name, overname, wname, NULL, parms, result, is_static);
        if (r != SWIG_OK) {
            return r;
        }

        if (Getattr(n, "sym:overloaded") && !Getattr(n, "sym:nextSibling")) {
            String *scope;
            if (!class_name || is_static || is_ctor_dtor) {
                scope = NULL;
            } else {
                scope = NewString("swiggoscope.");
                Append(scope, class_name);
            }
            if (!checkNameConflict(go_name, n, scope)) {
                Delete(go_name);
                return SWIG_NOWRAP;
            }

            String *receiver = class_receiver;
            if (is_static || is_ctor_dtor) {
                receiver = NULL;
            }
            r = makeDispatchFunction(n, go_name, receiver, is_static, NULL, false);
            if (r != SWIG_OK) {
                return r;
            }
        }

        Delete(wname);
        Delete(go_name);
        Delete(r1);

        return SWIG_OK;
    }

    /* ----------------------------------------------------------------------
   * staticmemberfunctionHandler()
   *
   * For some reason the language code removes the "storage" attribute
   * for a static function before calling functionWrapper, which means
   * that we have no way of knowing whether a function is static or
   * not.  That makes no sense in the Go context.  Here we note that a
   * function is static.
   * ---------------------------------------------------------------------- */

    int staticmemberfunctionHandler(Node *n) {
        assert(!is_static_member_function);
        is_static_member_function = true;
        int r = Language::staticmemberfunctionHandler(n);
        is_static_member_function = false;
        return r;
    }

    /* ----------------------------------------------------------------------
   * makeWrappers()
   *
   * Write out the various function wrappers.
   * n: The function we are emitting.
   * go_name: The name of the function in Go.
   * overname: The overload string for overloaded function.
   * wname: The SWIG wrapped name--the name of the C function.
   * base: A list of the names of base classes, in the case where this
   *       is a virtual method not defined in the current class.
   * parms: The parameters.
   * result: The result type.
   * is_static: Whether this is a static method or member.
   * ---------------------------------------------------------------------- */

    int makeWrappers(Node *n, String *go_name, String *overname, String *wname, List *base, ParmList *parms,
                     SwigType *result, bool is_static) {

        assert(result);

        int ret = SWIG_OK;

        int r = makeCgoWrappers(n, go_name, overname, wname, base, parms, result, is_static);
        if (r != SWIG_OK) {
            ret = r;
        }

        if (class_methods) {
            Setattr(class_methods, Getattr(n, "name"), NewString(""));
        }

        return ret;
    }

    /* ----------------------------------------------------------------------
   * struct cgoWrapperInfo
   *
   * Information needed by the CGO wrapper functions.
   * ---------------------------------------------------------------------- */

    struct cgoWrapperInfo {
        // The function we are generating code for.
        Node *n;
        // The name of the Go function.
        String *go_name;
        // The overload string for an overloaded function.
        String *overname;
        // The name of the C wrapper function.
        String *wname;
        // The base classes.
        List *base;
        // The parameters.
        ParmList *parms;
        // The result type.
        SwigType *result;
        // Whether this is a static function, not a class method.
        bool is_static;
        // The Go receiver type.
        String *receiver;
        // Whether this is a class constructor.
        bool is_constructor;
        // Whether this is a class destructor.
        bool is_destructor;
    };

    /* ----------------------------------------------------------------------
   * makeCgoWrappers()
   *
   * Write out the wrappers for a function when producing cgo input
   * files.
   * ---------------------------------------------------------------------- */

    int makeCgoWrappers(Node *n, String *go_name, String *overname, String *wname, List *base, ParmList *parms,
                        SwigType *result, bool is_static) {
        Swig_save("makeCgoWrappers", n, "emit:cgotype", "emit:cgotypestruct", NULL);

        cgoWrapperInfo info;

        info.n = n;
        info.go_name = go_name;
        info.overname = overname;
        info.wname = wname;
        info.base = base;
        info.parms = parms;
        info.result = result;
        info.is_static = is_static;

        info.receiver = class_receiver;
        if (is_static) {
            info.receiver = NULL;
        }

        String *nodetype = Getattr(n, "nodeType");
        info.is_constructor = Cmp(nodetype, "constructor") == 0;
        info.is_destructor = Cmp(nodetype, "destructor") == 0;
        if (info.is_constructor || info.is_destructor) {
            assert(class_receiver);
            assert(!base);
            info.receiver = NULL;
        }

        int ret = SWIG_OK;
        int parm_count = emit_num_arguments(info.parms);
        int required_count = emit_num_required(info.parms);
        int args = 0;
        Parm *p = info.parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            trySetLNameWithName(p);
            p = nextParm(p);
        }
        int r = cgoGoWrapper(&info);
        if (r != SWIG_OK) {
            ret = r;
        }

        r = cgoCommentWrapper(&info);
        if (r != SWIG_OK) {
            ret = r;
        }

        r = cgoGccWrapper(&info);
        if (r != SWIG_OK) {
            ret = r;
        }

        Swig_restore(n);

        return ret;
    }

    /* ----------------------------------------------------------------------
   * cgoGoWrapper()
   *
   * Write out Go code to call a cgo function.  This code will go into
   * the generated Go output file.
   * ---------------------------------------------------------------------- */
    int cgoGoWrapper(const cgoWrapperInfo *info) {

        Wrapper *dummy = initGoTypemaps(info->parms);

        bool add_to_interface =
                interfaces && !info->is_constructor && !info->is_destructor && !info->is_static && !info->overname &&
                checkFunctionVisibility(info->n, NULL);

        Printv(f_go_wrappers, "func ", NULL);

        Parm *p = info->parms;
        int pi = 0;

        // Add the receiver first if this is a method.
        if (info->receiver) {
            Printv(f_go_wrappers, "(", NULL);
            if (info->base && info->receiver) {
                Printv(f_go_wrappers, "_swig_base", NULL);
            } else {
                Printv(f_go_wrappers, Getattr(p, "lname"), NULL);
                p = nextParm(p);
                ++pi;
            }
            Printv(f_go_wrappers, " ", info->receiver, ") ", NULL);
        }

        Printv(f_go_wrappers, info->go_name, NULL);
        if (info->overname) {
            Printv(f_go_wrappers, info->overname, NULL);
        }
        Printv(f_go_wrappers, "(", NULL);

        // If we are doing methods, add this method to the interface.
        if (add_to_interface) {
            Printv(interfaces, "\t", info->go_name, "(", NULL);
        }

        // Write out the parameters to both the function definition and
        // the interface.

        String *parm_print = NewString("");

        int parm_count = emit_num_arguments(info->parms);
        int required_count = emit_num_required(info->parms);
        int args = 0;

        for (; pi < parm_count; ++pi) {
            p = getParm(p);
            trySetLNameWithName(p);
            if (pi == 0 && info->is_destructor) {
                String *cl = exportedName(class_name);
                Printv(parm_print, Getattr(p, "lname"), " ", cl, NULL);
                Delete(cl);
                ++args;
            } else {
                if (args > 0) {
                    Printv(parm_print, ", ", NULL);
                }
                ++args;
                if (pi >= required_count) {
                    Printv(parm_print, "_swig_args ...interface{}", NULL);
                    break;
                }

                Printv(parm_print, Getattr(p, "lname"), " ", NULL);
                String *tm = goType(p, Getattr(p, "type"));
                Printv(parm_print, tm, NULL);
                Delete(tm);
            }
            p = nextParm(p);
        }

        Printv(parm_print, ")", NULL);

        // Write out the result type.
        if (info->is_constructor) {
            String *cl = exportedName(class_name);
            Printv(parm_print, " (_swig_ret ", cl, ")", NULL);
            Delete(cl);
        } else {
            if (SwigType_type(info->result) != T_VOID) {
                String *tm = goType(info->n, info->result);
                Printv(parm_print, " (_swig_ret ", tm, ")", NULL);
                Delete(tm);
            }
        }

        Printv(f_go_wrappers, parm_print, NULL);
        if (add_to_interface) {
            Printv(interfaces, parm_print, "\n", NULL);
        }

        // Write out the function body.

        Printv(f_go_wrappers, " {\n", NULL);

        if (parm_count > required_count) {
            Parm *p = info->parms;
            int i;
            for (i = 0; i < required_count; ++i) {
                p = getParm(p);
                p = nextParm(p);
            }
            for (; i < parm_count; ++i) {
                p = getParm(p);
                String *tm = goType(p, Getattr(p, "type"));
                Printv(f_go_wrappers, "\tvar ", Getattr(p, "lname"), " ", tm, "\n", NULL);
                Printf(f_go_wrappers, "\tif len(_swig_args) > %d {\n", i - required_count);
                Printf(f_go_wrappers, "\t\t%s = _swig_args[%d].(%s)\n", Getattr(p, "lname"), i - required_count, tm);
                Printv(f_go_wrappers, "\t}\n", NULL);
                Delete(tm);
                p = nextParm(p);
            }
        }

        String *call = NewString("\t");

        String *ret_type = NULL;
        bool memcpy_ret = false;
        String *wt = NULL;
        if (SwigType_type(info->result) != T_VOID) {
            if (info->is_constructor) {
                ret_type = exportedName(class_name);
            } else {
                ret_type = goImType(info->n, info->result);
            }
            Printv(f_go_wrappers, "\tvar swig_r ", ret_type, "\n", NULL);

            bool c_struct_type;
            Delete(cgoTypeForGoValue(info->n, info->result, &c_struct_type));
            if (c_struct_type) {
                memcpy_ret = true;
            }

            if (memcpy_ret) {
                Printv(call, "swig_r_p := ", NULL);
            } else {
                Printv(call, "swig_r = (", ret_type, ")(", NULL);
            }

            if (info->is_constructor || goTypeIsInterface(info->n, info->result)) {
                if (info->is_constructor) {
                    wt = Copy(class_receiver);
                } else {
                    wt = goWrapperType(info->n, info->result, true);
                }
                Printv(call, wt, "(", NULL);
            }
        }

        Printv(call, "C.", info->wname, "(", NULL);

        args = 0;

        if (parm_count > required_count) {
            Printv(call, "C.swig_intgo(len(_swig_args))", NULL);
            ++args;
        }

        if (info->base && info->receiver) {
            if (args > 0) {
                Printv(call, ", ", NULL);
            }
            ++args;
            Printv(call, "C.uintptr_t(_swig_base)", NULL);
        }

        p = info->parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            if (args > 0) {
                Printv(call, ", ", NULL);
            }
            ++args;

            SwigType *pt = Getattr(p, "type");
            String *ln = Getattr(p, "lname");

            String *ivar = NewStringf("_swig_i_%d", i);

            String *goin = goGetattr(p, "tmap:goin");
            if (goin == NULL) {
                Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
                bool need_close = false;
                if ((i == 0 && info->is_destructor) ||
                    ((i > 0 || !info->receiver || info->base || info->is_constructor) && goTypeIsInterface(p, pt))) {
                    Printv(f_go_wrappers, "getSwigcptr(", NULL);
                    need_close = true;
                }
                Printv(f_go_wrappers, ln, NULL);
                if (need_close) {
                    Printv(f_go_wrappers, ")", NULL);
                }
                Printv(f_go_wrappers, "\n", NULL);
                Setattr(p, "emit:goinput", ln);
            } else {
                String *itm = goImType(p, pt);
                Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
                goin = Copy(goin);
                Replaceall(goin, "$input", ln);
                Replaceall(goin, "$result", ivar);
                Printv(f_go_wrappers, goin, "\n", NULL);
                Delete(goin);
                Setattr(p, "emit:goinput", ivar);
            }

            bool c_struct_type;
            String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
            if (c_struct_type) {
                Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
            } else {
                Printv(call, "C.", ct, "(", ivar, ")", NULL);
            }
            Delete(ct);

            p = nextParm(p);
        }

        Printv(f_go_wrappers, call, ")", NULL);
        Delete(call);

        if (wt) {
            // Close the type conversion to the wrapper type.
            Printv(f_go_wrappers, ")", NULL);
        }
        if (SwigType_type(info->result) != T_VOID && !memcpy_ret) {
            // Close the type conversion of the return value.
            Printv(f_go_wrappers, ")", NULL);
        }

        Printv(f_go_wrappers, "\n", NULL);

        if (memcpy_ret) {
            Printv(f_go_wrappers, "\tswig_r = *(*", ret_type, ")(unsafe.Pointer(&swig_r_p))\n", NULL);
        }
        if (ret_type) {
            Delete(ret_type);
        }

        goargout(info->parms);

        if (SwigType_type(info->result) != T_VOID) {

            Swig_save("cgoGoWrapper", info->n, "type", "tmap:goout", NULL);
            Setattr(info->n, "type", info->result);

            String *goout = goTypemapLookup("goout", info->n, "swig_r");
            if (goout == NULL) {
                Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
            } else {
                String *tm = goType(info->n, info->result);
                Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
                goout = Copy(goout);
                Replaceall(goout, "$input", "swig_r");
                Replaceall(goout, "$result", "swig_r_1");
                Printv(f_go_wrappers, goout, "\n", NULL);
                Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
            }

            Swig_restore(info->n);
        }

        Printv(f_go_wrappers, "}\n\n", NULL);

        DelWrapper(dummy);

        return SWIG_OK;
    }

    void trySetLNameWithName(void *p) const {
        String *ln = Getattr(p, "name");
        const char *pn = Char(ln);
        if (ln != nullptr) {
            if (Strcmp(pn, "type")==0) {
                ln = DohNewString("typex");
            }
            if (Strcmp(pn, "byte")==0) {
                ln = DohNewString("bytex");
            }
            if (Strcmp(pn, "struct")==0) {
                ln = DohNewString("structx");
            }
            Setattr(p, "lname", ln);
        }
    }

    /* ----------------------------------------------------------------------
   * cgoCommentWrapper()
   *
   * Write out a cgo function to call a C/C++ function.  This code
   * will go into the cgo comment in the generated Go output file.
   * ---------------------------------------------------------------------- */
    int cgoCommentWrapper(const cgoWrapperInfo *info) {
        String *ret_type;
        if (SwigType_type(info->result) == T_VOID) {
            ret_type = NewString("void");
        } else {
            bool c_struct_type;
            ret_type = cgoTypeForGoValue(info->n, info->result, &c_struct_type);
        }

        Printv(f_cgo_comment, "extern ", ret_type, " ", info->wname, "(", NULL);

        Delete(ret_type);

        int parm_count = emit_num_arguments(info->parms);
        int required_count = emit_num_required(info->parms);
        int args = 0;

        if (parm_count > required_count) {
            Printv(f_cgo_comment, "intgo _swig_args", NULL);
            ++args;
        }

        if (info->base && info->receiver) {
            if (args > 0) {
                Printv(f_cgo_comment, ", ", NULL);
            }
            ++args;
            Printv(f_cgo_comment, "uintptr_t _swig_base", NULL);
        }

        Parm *p = info->parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            if (args > 0) {
                Printv(f_cgo_comment, ", ", NULL);
            }
            ++args;

            SwigType *pt = Getattr(p, "type");
            String *ln = Getattr(p, "lname");

            bool c_struct_type;
            String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
            Printv(f_cgo_comment, ct, " ", ln, NULL);
            Delete(ct);

            p = nextParm(p);
        }

        if (args == 0) {
            Printv(f_cgo_comment, "void", NULL);
        }

        Printv(f_cgo_comment, ");\n", NULL);

        return SWIG_OK;
    }

    /* ----------------------------------------------------------------------
   * cgoGccWrapper()
   *
   * Write out code to the C/C++ wrapper file.  This code will be
   * called by the code generated by cgoCommentWrapper.
   * ---------------------------------------------------------------------- */
    int cgoGccWrapper(const cgoWrapperInfo *info) {
        Wrapper *f = NewWrapper();

        Swig_save("cgoGccWrapper", info->n, "parms", NULL);

        ParmList *parms = info->parms;

        Parm *base_parm = NULL;
        if (info->base && !isStatic(info->n)) {
            SwigType *base_type = Copy(getClassType());
            SwigType_add_pointer(base_type);
            base_parm = NewParm(base_type, NewString("arg1"), info->n);
            set_nextSibling(base_parm, parms);
            parms = base_parm;
        }

        emit_parameter_variables(parms, f);
        emit_attach_parmmaps(parms, f);
        int parm_count = emit_num_arguments(parms);
        int required_count = emit_num_required(parms);

        emit_return_variable(info->n, info->result, f);

        // Start the function definition.

        String *fnname = NewString("");
        Printv(fnname, info->wname, "(", NULL);

        int args = 0;

        if (parm_count > required_count) {
            Printv(fnname, "intgo _swig_optargc", NULL);
            ++args;
        }

        Parm *p = parms;
        for (int i = 0; i < parm_count; ++i) {
            if (args > 0) {
                Printv(fnname, ", ", NULL);
            }
            ++args;

            p = getParm(p);

            SwigType *pt = Copy(Getattr(p, "type"));
            if (SwigType_isarray(pt) && Getattr(p, "tmap:gotype") == NULL) {
                SwigType_del_array(pt);
                SwigType_add_pointer(pt);
            }
            String *pn = NewStringf("_swig_go_%d", i);
            String *ct = gcCTypeForGoValue(p, pt, pn);
            Printv(fnname, ct, NULL);
            Delete(ct);
            Delete(pn);
            Delete(pt);

            p = nextParm(p);
        }

        Printv(fnname, ")", NULL);

        if (SwigType_type(info->result) == T_VOID) {
            Printv(f->def, "void ", fnname, NULL);
        } else {
            String *ct = gcCTypeForGoValue(info->n, info->result, fnname);
            Printv(f->def, ct, NULL);
            Delete(ct);

            String *ln = NewString("_swig_go_result");
            ct = gcCTypeForGoValue(info->n, info->result, ln);
            Wrapper_add_local(f, "_swig_go_result", ct);
            Delete(ct);
            Delete(ln);
        }

        Delete(fnname);

        Printv(f->def, " {\n", NULL);

        // Apply the in typemaps.

        p = parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            String *tm = Getattr(p, "tmap:in");
            if (!tm) {
                Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number,
                             "unable to use type %s as a function argument\n", SwigType_str(Getattr(p, "type"), 0));
            } else {
                tm = Copy(tm);
                String *pn = NewStringf("_swig_go_%d", i);
                Replaceall(tm, "$input", pn);
                if (i < required_count) {
                    Printv(f->code, "\t", tm, "\n", NULL);
                } else {
                    Printf(f->code, "\tif (_swig_optargc > %d) {\n", i - required_count);
                    Printv(f->code, "\t\t", tm, "\n", NULL);
                    Printv(f->code, "\t}\n", NULL);
                }
                Delete(tm);
                Setattr(p, "emit:input", pn);
            }
            p = nextParm(p);
        }

        Printv(f->code, "\n", NULL);

        // Do the real work of the function.

        checkConstraints(parms, f);

        emitGoAction(info->n, info->base, parms, info->result, f);

        argout(parms, f);

        cleanupFunction(info->n, f, parms);

        if (SwigType_type(info->result) != T_VOID) {
            Printv(f->code, "\treturn _swig_go_result;\n", NULL);
        }

        Printv(f->code, "}\n", NULL);

        Wrapper_print(f, f_c_wrappers);

        Swig_restore(info->n);

        DelWrapper(f);
        if (base_parm) {
            Delete(base_parm);
        }

        return SWIG_OK;
    }

    /* ----------------------------------------------------------------------
   * initGoTypemaps()
   *
   * Initialize the typenames for a Go wrapper, returning a dummy
   * Wrapper*.  Also set consistent names for the parameters.
   * ---------------------------------------------------------------------- */

    Wrapper *initGoTypemaps(ParmList *parms) {
        Wrapper *dummy = NewWrapper();
        emit_attach_parmmaps(parms, dummy);

        Parm *p = parms;
        int parm_count = emit_num_arguments(parms);
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            Swig_cparm_name(p, i);
            p = nextParm(p);
        }

        Swig_typemap_attach_parms("default", parms, dummy);
        Swig_typemap_attach_parms("gotype", parms, dummy);
        Swig_typemap_attach_parms("goin", parms, dummy);
        Swig_typemap_attach_parms("goargout", parms, dummy);
        Swig_typemap_attach_parms("imtype", parms, dummy);

        return dummy;
    }

    /* -----------------------------------------------------------------------
   * checkConstraints()
   *
   * Check parameter constraints if any.  This is used for the C/C++
   * function.  This assumes that each parameter has an "emit:input"
   * property with the name to use to refer to that parameter.
   * ----------------------------------------------------------------------- */

    void checkConstraints(ParmList *parms, Wrapper *f) {
        Parm *p = parms;
        while (p) {
            String *tm = Getattr(p, "tmap:check");
            if (!tm) {
                p = nextSibling(p);
            } else {
                tm = Copy(tm);
                Replaceall(tm, "$input", Getattr(p, "emit:input"));
                Printv(f->code, tm, "\n\n", NULL);
                Delete(tm);
                p = Getattr(p, "tmap:check:next");
            }
        }
    }

    /* -----------------------------------------------------------------------
   * emitGoAction()
   *
   * Emit the action of the function.  This is used for the C/C++ function.
   * ----------------------------------------------------------------------- */

    void emitGoAction(Node *n, List *base, ParmList *parms, SwigType *result, Wrapper *f) {
        String *actioncode;
        if (!base || isStatic(n)) {
            Swig_director_emit_dynamic_cast(n, f);
            actioncode = emit_action(n);
        } else {
            // Call the base class method.
            actioncode = NewString("");

            String *current = NewString("");
            Printv(current, Getattr(parms, "lname"), NULL);

            int vc = 0;
            for (Iterator bi = First(base); bi.item; bi = Next(bi)) {
                Printf(actioncode, "  %s *swig_b%d = (%s *)%s;\n", bi.item, vc, bi.item, current);
                Delete(current);
                current = NewString("");
                Printf(current, "swig_b%d", vc);
                ++vc;
            }

            String *code = Copy(Getattr(n, "wrap:action"));
            Replace(code, Getattr(parms, "lname"), current, DOH_REPLACE_ANY | DOH_REPLACE_ID);
            Delete(current);
            Printv(actioncode, code, "\n", NULL);
        }

        Swig_save("emitGoAction", n, "type", "tmap:out", NULL);

        Setattr(n, "type", result);

        String *tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode);
        if (!tm) {
            Swig_warning(WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s\n",
                         SwigType_str(result, 0));
        } else {
            Replaceall(tm, "$result", "_swig_go_result");
            if (GetFlag(n, "feature:new")) {
                Replaceall(tm, "$owner", "1");
            } else {
                Replaceall(tm, "$owner", "0");
            }
            Printv(f->code, tm, "\n", NULL);
            Delete(tm);
        }

        Swig_restore(n);
    }

    /* -----------------------------------------------------------------------
   * argout()
   *
   * Handle argument output code if any.  This is used for the C/C++
   * function.  This assumes that each parameter has an "emit:input"
   * property with the name to use to refer to that parameter.
   * ----------------------------------------------------------------------- */

    void argout(ParmList *parms, Wrapper *f) {
        Parm *p = parms;
        while (p) {
            String *tm = Getattr(p, "tmap:argout");
            if (!tm) {
                p = nextSibling(p);
            } else {
                tm = Copy(tm);
                Replaceall(tm, "$result", Swig_cresult_name());
                Replaceall(tm, "$input", Getattr(p, "emit:input"));
                Printv(f->code, tm, "\n", NULL);
                Delete(tm);
                p = Getattr(p, "tmap:argout:next");
            }
        }
    }

    /* -----------------------------------------------------------------------
   * goargout()
   *
   * Handle Go argument output code if any.  This is used for the Go
   * function.  This assumes that each parameter has an "emit:goinput"
   * property with the name to use to refer to that parameter.
   * ----------------------------------------------------------------------- */

    void goargout(ParmList *parms) {
        Parm *p = parms;
        while (p) {
            String *tm = Getattr(p, "tmap:goargout");
            if (!tm) {
                p = nextSibling(p);
            } else {
                tm = Copy(tm);
                Replaceall(tm, "$result", "swig_r");
                Replaceall(tm, "$input", Getattr(p, "emit:goinput"));
                Printv(f_go_wrappers, tm, "\n", NULL);
                Delete(tm);
                p = Getattr(p, "tmap:goargout:next");
            }
        }

        // If we need to memcpy a parameter to pass it to the C code, the
        // compiler may think that the parameter is not live during the
        // function call.  If the garbage collector runs while the C/C++
        // function is running, the parameter may be freed.  Force the
        // compiler to see the parameter as live across the C/C++ function.
        int parm_count = emit_num_arguments(parms);
        p = parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            bool c_struct_type;
            Delete(cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type));
            if (c_struct_type) {
                Printv(f_go_wrappers, "\tif Swig_escape_always_false {\n", NULL);
                Printv(f_go_wrappers, "\t\tSwig_escape_val = ", Getattr(p, "emit:goinput"), "\n", NULL);
                Printv(f_go_wrappers, "\t}\n", NULL);
            }
            p = nextParm(p);
        }
    }

    /* -----------------------------------------------------------------------
   * freearg()
   *
   * Handle argument cleanup code if any.  This is used for the C/C++
   * function.  This assumes that each parameter has an "emit:input"
   * property with the name to use to refer to that parameter.
   * ----------------------------------------------------------------------- */

    String *freearg(ParmList *parms) {
        String *ret = NewString("");
        Parm *p = parms;
        while (p) {
            String *tm = Getattr(p, "tmap:freearg");
            if (!tm) {
                p = nextSibling(p);
            } else {
                tm = Copy(tm);
                Replaceall(tm, "$input", Getattr(p, "emit:input"));
                Printv(ret, tm, "\n", NULL);
                Delete(tm);
                p = Getattr(p, "tmap:freearg:next");
            }
        }
        return ret;
    }

    /* -----------------------------------------------------------------------
   * cleanupFunction()
   *
   * Final function cleanup code.
   * ----------------------------------------------------------------------- */

    void cleanupFunction(Node *n, Wrapper *f, ParmList *parms) {
        String *cleanup = freearg(parms);
        Printv(f->code, cleanup, NULL);

        if (GetFlag(n, "feature:new")) {
            String *tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0);
            if (tm) {
                Printv(f->code, tm, "\n", NULL);
                Delete(tm);
            }
        }

        Replaceall(f->code, "$cleanup", cleanup);
        Delete(cleanup);

        /* See if there is any return cleanup code */
        String *tm;
        if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
            Printf(f->code, "%s\n", tm);
            Delete(tm);
        }

        Replaceall(f->code, "$symname", Getattr(n, "sym:name"));
    }

    /* -----------------------------------------------------------------------
   * variableHandler()
   *
   * This exists just to set the making_variable_wrappers flag.
   * ----------------------------------------------------------------------- */

    virtual int variableHandler(Node *n) {
        assert(!making_variable_wrappers);
        making_variable_wrappers = true;
        int r = Language::variableHandler(n);
        making_variable_wrappers = false;
        return r;
    }

    /* -----------------------------------------------------------------------
   * constantWrapper()
   *
   * Product a const declaration.
   * ------------------------------------------------------------------------ */

    virtual int constantWrapper(Node *n) {
        SwigType *type = Getattr(n, "type");

        if (!SwigType_issimple(type) && SwigType_type(type) != T_STRING) {
            return goComplexConstant(n, type);
        }

        if (Swig_storage_isstatic(n)) {
            return goComplexConstant(n, type);
        }

        String *go_name = buildGoName(Getattr(n, "sym:name"), false, false);

        String *tm = goType(n, type);
        String *value = Getattr(n, "value");

        String *copy = NULL;
        if (SwigType_type(type) == T_BOOL) {
            if (Cmp(value, "true") != 0 && Cmp(value, "false") != 0) {
                return goComplexConstant(n, type);
            }
        } else if (SwigType_type(type) == T_STRING || SwigType_type(type) == T_CHAR) {
            // Backslash sequences are somewhat different in Go and C/C++.
            if (Strchr(value, '\\') != 0) {
                return goComplexConstant(n, type);
            }
        } else {
            // Accept a 0x prefix, and strip combinations of u and l
            // suffixes.  Otherwise accept digits, decimal point, and
            // exponentiation.  Treat anything else as too complicated to
            // handle as a Go constant.
            char *p = Char(value);
            int len = (int) strlen(p);
            bool need_copy = false;
            while (len > 0) {
                char c = p[len - 1];
                if (c != 'l' && c != 'L' && c != 'u' && c != 'U') {
                    break;
                }
                --len;
                need_copy = true;
            }
            bool is_hex = false;
            int i = 0;
            if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
                i = 2;
                is_hex = true;
            }
            for (; i < len; ++i) {
                switch (p[i]) {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        break;
                    case 'a':
                    case 'b':
                    case 'c':
                    case 'd':
                    case 'f':
                    case 'A':
                    case 'B':
                    case 'C':
                    case 'D':
                    case 'F':
                        if (!is_hex) {
                            return goComplexConstant(n, type);
                        }
                        break;
                    case '.':
                    case 'e':
                    case 'E':
                    case '+':
                    case '-':
                        break;
                    default:
                        return goComplexConstant(n, type);
                }
            }
            if (need_copy) {
                copy = Copy(value);
                Replaceall(copy, p + len, "");
                value = copy;
            }
        }

        if (!checkNameConflict(go_name, n, NULL)) {
            Delete(tm);
            Delete(go_name);
            Delete(copy);
            return SWIG_NOWRAP;
        }

        Printv(f_go_wrappers, "const ", go_name, " ", tm, " = ", NULL);
        if (SwigType_type(type) == T_STRING) {
            Printv(f_go_wrappers, "\"", value, "\"", NULL);
        } else if (SwigType_type(type) == T_CHAR) {
            Printv(f_go_wrappers, "'", value, "'", NULL);
        } else {
            Printv(f_go_wrappers, value, NULL);
        }

        Printv(f_go_wrappers, "\n", NULL);

        Delete(tm);
        Delete(go_name);
        Delete(copy);

        return SWIG_OK;
    }

    /* ----------------------------------------------------------------------
   * enumDeclaration()
   *
   * A C++ enum type turns into a Named go int type.
   * ---------------------------------------------------------------------- */

    virtual int enumDeclaration(Node *n) {
        if (getCurrentClass() && (cplus_mode != PUBLIC))
            return SWIG_NOWRAP;

        String *name = goEnumName(n);
        if (Strcmp(name, "int") != 0) {
            if (!ImportMode || !imported_package) {
                if (!checkNameConflict(name, n, NULL)) {
                    Delete(name);
                    return SWIG_NOWRAP;
                }
                Printv(f_go_wrappers, "type ", name, " int\n", NULL);
            } else {
                String *nw = NewString("");
                Printv(nw, getModuleName(imported_package), ".", name, NULL);
                Setattr(n, "go:enumname", nw);
            }
        }
        Delete(name);

        return Language::enumDeclaration(n);
    }

    /* -----------------------------------------------------------------------
   * enumvalueDeclaration()
   *
   * Declare a single value of an enum type.  We fetch the value by
   * calling a C/C++ function.
   * ------------------------------------------------------------------------ */

    virtual int enumvalueDeclaration(Node *n) {
        if (!is_public(n)) {
            return SWIG_OK;
        }

        Swig_require("enumvalueDeclaration", n, "*sym:name", NIL);
        Node *parent = parentNode(n);

        if (Getattr(parent, "unnamed")) {
            Setattr(n, "type", NewString("int"));
        } else {
            Setattr(n, "type", Getattr(parent, "enumtype"));
        }

        if (GetFlag(parent, "scopedenum")) {
            String *symname = Getattr(n, "sym:name");
            symname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
            Setattr(n, "sym:name", symname);
            Delete(symname);
        }

        int ret = goComplexConstant(n, Getattr(n, "type"));
        Swig_restore(n);
        return ret;
    }

    /* -----------------------------------------------------------------------
   * goComplexConstant()
   *
   * Handle a const declaration for something which is not a Go constant.
   * ------------------------------------------------------------------------ */

    int goComplexConstant(Node *n, SwigType *type) {
        String *symname = Getattr(n, "sym:name");
        if (!symname) {
            symname = Getattr(n, "name");
        }

        String *varname = buildGoName(symname, true, false);

        if (!checkNameConflict(varname, n, NULL)) {
            Delete(varname);
            return SWIG_NOWRAP;
        }

        String *rawval = Getattr(n, "rawval");
        if (rawval && Len(rawval)) {
            // Based on Swig_VargetToFunction
            String *nname = NewStringf("(%s)", rawval);
            String *call;
            if (SwigType_isclass(type)) {
                call = NewStringf("%s", nname);
            } else {
                call = SwigType_lcaststr(type, nname);
            }
            String *cres = Swig_cresult(type, Swig_cresult_name(), call);
            Setattr(n, "wrap:action", cres);
            Delete(nname);
            Delete(call);
            Delete(cres);
        } else {
            String *get = NewString("");
            Printv(get, Swig_cresult_name(), " = ", NULL);

            char quote;
            if (Getattr(n, "wrappedasconstant")) {
                quote = '\0';
            } else if (SwigType_type(type) == T_CHAR) {
                quote = '\'';
            } else if (SwigType_type(type) == T_STRING) {
                Printv(get, "(char *)", NULL);
                quote = '"';
            } else {
                quote = '\0';
            }

            if (quote != '\0') {
                Printf(get, "%c", quote);
            }

            Printv(get, Getattr(n, "value"), NULL);

            if (quote != '\0') {
                Printf(get, "%c", quote);
            }

            Printv(get, ";\n", NULL);

            Setattr(n, "wrap:action", get);
            Delete(get);
        }

        String *sname = Copy(symname);
        if (class_name) {
            Append(sname, "_");
            Append(sname, class_name);
        }

        String *go_name = NewString("_swig_get");
        if (class_name) {
            Append(go_name, class_name);
            Append(go_name, "_");
        }
        Append(go_name, sname);

        String *wname = Swig_name_wrapper(sname);
        Append(wname, unique_id);
        Setattr(n, "wrap:name", wname);

        int r = makeWrappers(n, go_name, NULL, wname, NULL, NULL, type, true);

        if (r != SWIG_OK) {
            return r;
        }

        String *t = goType(n, type);
        Printv(f_go_wrappers, "var ", varname, " ", t, " = ", go_name, "()\n", NULL);

        Delete(varname);
        Delete(t);
        Delete(go_name);
        Delete(sname);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * classHandler()
   *
   * For a C++ class, in Go we generate both a struct and an
   * interface.  The interface will declare all the class public
   * methods.  We will define all the methods on the struct, so that
   * the struct meets the interface.  We then expect users of the
   * class to use the interface.
   * ------------------------------------------------------------ */

    virtual int classHandler(Node *n) {
        class_node = n;

        List *baselist = Getattr(n, "bases");
        bool has_base_classes = baselist && Len(baselist) > 0;

        String *name = Getattr(n, "sym:name");

        String *go_name = exportedName(name);

        if (!checkNameConflict(go_name, n, NULL)) {
            Delete(go_name);
            SetFlag(n, "go:conflict");
            return SWIG_NOWRAP;
        }

        String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);

        class_name = name;
        class_receiver = go_type_name;
        class_methods = NewHash();

        int isdir = GetFlag(n, "feature:director");
        int isnodir = GetFlag(n, "feature:nodirector");
        bool is_director = isdir && !isnodir;

        Printv(f_go_wrappers, "type ", go_type_name, " uintptr\n\n", NULL);

        // A method to return the pointer to the C++ class.  This is used
        // by generated code to convert between the interface and the C++
        // value.
        Printv(f_go_wrappers, "func (p ", go_type_name, ") Swigcptr() uintptr {\n", NULL);
        Printv(f_go_wrappers, "\treturn (uintptr)(p)\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        // A method used as a marker for the class, to avoid invalid
        // interface conversions when using multiple inheritance.
        Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigIs", go_name, "() {\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        if (is_director) {
            // Return the interface passed to the NewDirector function.
            Printv(f_go_wrappers, "func (p ", go_type_name, ") DirectorInterface() interface{} {\n", NULL);
            Printv(f_go_wrappers, "\treturn nil\n", NULL);
            Printv(f_go_wrappers, "}\n\n", NULL);
        }

        // We have seen a definition for this type.
        Setattr(defined_types, go_name, go_name);
        Setattr(defined_types, go_type_name, go_type_name);

        interfaces = NewString("");

        int r = Language::classHandler(n);
        if (r != SWIG_OK) {
            return r;
        }

        if (has_base_classes) {
            // For each method defined in a base class but not defined in
            // this class, we need to define the method in this class.  We
            // can't use anonymous field inheritance because it works
            // differently in Go and in C++.

            Hash *local = NewHash();
            for (Node *ni = Getattr(n, "firstChild"); ni; ni = nextSibling(ni)) {

                if (!is_public(ni)) {
                    continue;
                }

                String *type = Getattr(ni, "nodeType");
                if (Cmp(type, "constructor") == 0 || Cmp(type, "destructor") == 0) {
                    continue;
                }

                String *cname = Getattr(ni, "sym:name");
                if (!cname) {
                    cname = Getattr(ni, "name");
                }
                if (cname) {
                    Setattr(local, cname, NewString(""));
                }
            }

            for (Iterator b = First(baselist); b.item; b = Next(b)) {
                List *bases = NewList();
                Append(bases, Getattr(b.item, "classtype"));
                int r = addBase(n, b.item, bases, local);
                if (r != SWIG_OK) {
                    return r;
                }
                Delete(bases);
            }

            Delete(local);

            Hash *parents = NewHash();
            addFirstBaseInterface(n, parents, baselist);
            int r = addExtraBaseInterfaces(n, parents, baselist);
            Delete(parents);
            if (r != SWIG_OK) {
                return r;
            }
        }

        Printv(f_go_wrappers, "type ", go_name, " interface {\n", NULL);
        Printv(f_go_wrappers, "\tSwigcptr() uintptr\n", NULL);
        Printv(f_go_wrappers, "\tSwigIs", go_name, "()\n", NULL);

        if (is_director) {
            Printv(f_go_wrappers, "\tDirectorInterface() interface{}\n", NULL);
        }

        Append(f_go_wrappers, interfaces);
        Printv(f_go_wrappers, "}\n\n", NULL);
        Delete(interfaces);

        interfaces = NULL;
        class_name = NULL;
        class_receiver = NULL;
        class_node = NULL;
        Delete(class_methods);
        class_methods = NULL;

        Delete(go_type_name);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * addBase()
   *
   * Implement methods and members defined in a parent class for a
   * child class.
   * ------------------------------------------------------------ */

    int addBase(Node *n, Node *base, List *bases, Hash *local) {
        if (GetFlag(base, "feature:ignore")) {
            return SWIG_OK;
        }

        for (Node *ni = Getattr(base, "firstChild"); ni; ni = nextSibling(ni)) {
            int r = goBaseEntry(n, bases, local, ni);
            if (r != SWIG_OK) {
                return r;
            }
        }

        List *baselist = Getattr(base, "bases");
        if (baselist && Len(baselist) > 0) {
            for (Iterator b = First(baselist); b.item; b = Next(b)) {
                List *nb = Copy(bases);
                Append(nb, Getattr(b.item, "classtype"));
                int r = addBase(n, b.item, nb, local);
                Delete(nb);
                if (r != SWIG_OK) {
                    return r;
                }
            }
        }

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * goBaseEntry()
   *
   * Implement one entry defined in a parent class for a child class.
   * n is the child class.
   * ------------------------------------------------------------ */

    int goBaseEntry(Node *n, List *bases, Hash *local, Node *entry) {
        if (GetFlag(entry, "feature:ignore")) {
            return SWIG_OK;
        }

        if (!is_public(entry)) {
            return SWIG_OK;
        }

        String *type = Getattr(entry, "nodeType");
        if (Strcmp(type, "constructor") == 0 || Strcmp(type, "destructor") == 0 || Strcmp(type, "enum") == 0 ||
            Strcmp(type, "using") == 0 || Strcmp(type, "classforward") == 0 || Strcmp(type, "template") == 0) {
            return SWIG_OK;
        }

        if (Strcmp(type, "extend") == 0) {
            for (Node *extend = firstChild(entry); extend; extend = nextSibling(extend)) {
                if (isStatic(extend)) {
                    // If we don't do this, the extend_default test case fails.
                    continue;
                }

                int r = goBaseEntry(n, bases, local, extend);
                if (r != SWIG_OK) {
                    return r;
                }
            }
            return SWIG_OK;
        }

        String *storage = Getattr(entry, "storage");
        if (storage && (Strcmp(storage, "typedef") == 0 || Strstr(storage, "friend"))) {
            return SWIG_OK;
        }

        String *mname = Getattr(entry, "sym:name");
        if (!mname) {
            return SWIG_OK;
        }

        String *lname = Getattr(entry, "name");
        if (Getattr(class_methods, lname)) {
            return SWIG_OK;
        }
        if (Getattr(local, lname)) {
            return SWIG_OK;
        }
        Setattr(local, lname, NewString(""));

        String *ty = NewString(Getattr(entry, "type"));
        SwigType_push(ty, Getattr(entry, "decl"));
        String *fullty = SwigType_typedef_resolve_all(ty);
        bool is_function = SwigType_isfunction(fullty) ? true : false;
        Delete(ty);
        Delete(fullty);

        if (is_function) {
            int r = goBaseMethod(n, bases, entry);
            if (r != SWIG_OK) {
                return r;
            }

            if (Getattr(entry, "sym:overloaded")) {
                for (Node *on = Getattr(entry, "sym:nextSibling"); on; on = Getattr(on, "sym:nextSibling")) {
                    r = goBaseMethod(n, bases, on);
                    if (r != SWIG_OK) {
                        return r;
                    }
                }

                String *receiver = class_receiver;
                bool is_static = isStatic(entry);
                if (is_static) {
                    receiver = NULL;
                }
                String *go_name = buildGoName(Getattr(entry, "sym:name"), is_static, false);
                r = makeDispatchFunction(entry, go_name, receiver, is_static, NULL, false);
                Delete(go_name);
                if (r != SWIG_OK) {
                    return r;
                }
            }
        } else {
            int r = goBaseVariable(n, bases, entry);
            if (r != SWIG_OK) {
                return r;
            }
        }

        return SWIG_OK;
    }


    /* ------------------------------------------------------------
   * goBaseMethod()
   *
   * Implement a method defined in a parent class for a child class.
   * ------------------------------------------------------------ */

    int goBaseMethod(Node *method_class, List *bases, Node *method) {
        String *symname = Getattr(method, "sym:name");
        if (!validIdentifier(symname)) {
            return SWIG_OK;
        }

        String *name = NewString("");
        Printv(name, Getattr(method_class, "sym:name"), "_", symname, NULL);

        bool is_static = isStatic(method);

        String *go_name = buildGoName(name, is_static, false);

        String *overname = NULL;
        if (Getattr(method, "sym:overloaded")) {
            overname = Getattr(method, "sym:overname");
        }
        String *wname = Swig_name_wrapper(name);
        if (overname) {
            Append(wname, overname);
        }
        Append(wname, unique_id);

        String *result = NewString(Getattr(method, "type"));
        SwigType_push(result, Getattr(method, "decl"));
        if (SwigType_isqualifier(result)) {
            Delete(SwigType_pop(result));
        }
        Delete(SwigType_pop_function(result));

        // If the base method is imported, wrap:action may not be set.
        Swig_save("goBaseMethod", method, "wrap:name", "wrap:action", "parms", NULL);
        Setattr(method, "wrap:name", wname);
        if (!Getattr(method, "wrap:action")) {
            if (!is_static) {
                Swig_MethodToFunction(method, getNSpace(), getClassType(),
                                      (Getattr(method, "template") ? SmartPointer : Extend | SmartPointer), NULL,
                                      false);
                // Remove any self parameter that was just added.
                ParmList *parms = Getattr(method, "parms");
                if (parms && Getattr(parms, "self")) {
                    parms = CopyParmList(nextSibling(parms));
                    Setattr(method, "parms", parms);
                }
            } else {
                String *call = Swig_cfunction_call(Getattr(method, "name"), Getattr(method, "parms"));
                Setattr(method, "wrap:action", Swig_cresult(Getattr(method, "type"), Swig_cresult_name(), call));
            }
        }

        // A method added by %extend in a base class may have void parms.
        ParmList *parms = Getattr(method, "parms");
        if (parms != NULL && SwigType_type(Getattr(parms, "type")) == T_VOID) {
            parms = NULL;
        }

        int r = makeWrappers(method, go_name, overname, wname, bases, parms, result, is_static);

        Swig_restore(method);

        Delete(result);
        Delete(go_name);
        Delete(name);

        return r;
    }

    /* ------------------------------------------------------------
   * goBaseVariable()
   *
   * Add accessors for a member variable defined in a parent class for
   * a child class.
   * ------------------------------------------------------------ */

    int goBaseVariable(Node *var_class, List *bases, Node *var) {
        if (isStatic(var)) {
            return SWIG_OK;
        }

        String *var_name = buildGoName(Getattr(var, "sym:name"), false, false);

        Swig_save("goBaseVariable", var, "type", "wrap:action", NULL);

        // For a pointer type we apparently have to wrap in the decl.
        SwigType *var_type = NewString(Getattr(var, "type"));
        SwigType_push(var_type, Getattr(var, "decl"));
        Setattr(var, "type", var_type);

        SwigType *vt = Copy(var_type);

        int flags = Extend | SmartPointer | use_naturalvar_mode(var);
        if (isNonVirtualProtectedAccess(var)) {
            flags |= CWRAP_ALL_PROTECTED_ACCESS;
        }

        // Copied from Swig_wrapped_member_var_type.
        if (SwigType_isclass(vt)) {
            if (flags & CWRAP_NATURAL_VAR) {
                if (CPlusPlus) {
                    if (!SwigType_isconst(vt)) {
                        SwigType_add_qualifier(vt, "const");
                    }
                    SwigType_add_reference(vt);
                }
            } else {
                SwigType_add_pointer(vt);
            }
        }

        String *mname = Swig_name_member(getNSpace(), Getattr(var_class, "sym:name"), var_name);

        if (!is_immutable(var)) {
            for (Iterator ki = First(var); ki.key; ki = Next(ki)) {
                if (Strncmp(ki.key, "tmap:", 5) == 0) {
                    Delattr(var, ki.key);
                }
            }
            Swig_save("goBaseVariableSet", var, "name", "sym:name", "type", NULL);

            String *mname_set = NewString("Set");
            Append(mname_set, mname);

            String *go_name = NewString("Set");
            Append(go_name, var_name);

            Swig_MembersetToFunction(var, class_name, flags);

            String *wname = Swig_name_wrapper(mname_set);
            Append(wname, unique_id);
            ParmList *parms = NewParm(vt, var_name, var);
            String *result = NewString("void");
            int r = makeWrappers(var, go_name, NULL, wname, bases, parms, result, false);
            if (r != SWIG_OK) {
                return r;
            }
            Delete(wname);
            Delete(parms);
            Delete(result);
            Delete(go_name);
            Delete(mname_set);

            Swig_restore(var);
            for (Iterator ki = First(var); ki.key; ki = Next(ki)) {
                if (Strncmp(ki.key, "tmap:", 5) == 0) {
                    Delattr(var, ki.key);
                }
            }
        }

        Swig_MembergetToFunction(var, class_name, flags);

        String *mname_get = NewString("Get");
        Append(mname_get, mname);

        String *go_name = NewString("Get");
        Append(go_name, var_name);

        String *wname = Swig_name_wrapper(mname_get);
        Append(wname, unique_id);

        int r = makeWrappers(var, go_name, NULL, wname, bases, NULL, vt, false);
        if (r != SWIG_OK) {
            return r;
        }

        Delete(wname);
        Delete(mname_get);
        Delete(go_name);
        Delete(mname);
        Delete(var_name);
        Delete(var_type);
        Delete(vt);

        Swig_restore(var);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * addFirstBaseInterface()
   *
   * When a C++ class uses multiple inheritance, we can use the C++
   * pointer for the first base class but not for any subsequent base
   * classes.  However, the Go interface will match the interface for
   * all the base classes.  To avoid accidentally treating a class as
   * a pointer to a base class other than the first one, we use an
   * isClassname method.  This function adds those methods as
   * required.
   *
   * For convenience when using multiple inheritance, we also add
   * functions to retrieve the base class pointers.
   * ------------------------------------------------------------ */

    void addFirstBaseInterface(Node *n, Hash *parents, List *bases) {
        if (!bases || Len(bases) == 0) {
            return;
        }
        Iterator b = First(bases);
        if (!GetFlag(b.item, "feature:ignore")) {
            String *go_name = buildGoName(Getattr(n, "sym:name"), false, false);
            String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);
            String *go_base_name = exportedName(Getattr(b.item, "sym:name"));
            String *go_base_type = goType(n, Getattr(b.item, "classtypeobj"));
            String *go_base_type_name = goCPointerType(Getattr(b.item, "classtypeobj"), true);

            Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigIs", go_base_name, "() {\n", NULL);
            Printv(f_go_wrappers, "}\n\n", NULL);

            Printv(interfaces, "\tSwigIs", go_base_name, "()\n", NULL);

            Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigGet", go_base_name, "() ", go_base_type, " {\n",
                   NULL);
            Printv(f_go_wrappers, "\treturn ", go_base_type_name, "(getSwigcptr(p))\n", NULL);
            Printv(f_go_wrappers, "}\n\n", NULL);

            Printv(interfaces, "\tSwigGet", go_base_name, "() ", go_base_type, "\n", NULL);

            Setattr(parents, go_base_name, NewString(""));

            Delete(go_name);
            Delete(go_type_name);
            Delete(go_base_type);
            Delete(go_base_type_name);
        }

        addFirstBaseInterface(n, parents, Getattr(b.item, "bases"));
    }

    /* ------------------------------------------------------------
   * addExtraBaseInterfaces()
   *
   * Add functions to retrieve the base class pointers for all base
   * classes other than the first.
   * ------------------------------------------------------------ */

    int addExtraBaseInterfaces(Node *n, Hash *parents, List *bases) {
        Iterator b = First(bases);

        Node *fb = b.item;

        for (b = Next(b); b.item; b = Next(b)) {
            if (GetFlag(b.item, "feature:ignore")) {
                continue;
            }

            String *go_base_name = exportedName(Getattr(b.item, "sym:name"));

            Swig_save("addExtraBaseInterface", n, "wrap:action", "wrap:name", "wrap:parms", NULL);

            SwigType *type = Copy(Getattr(n, "classtypeobj"));
            SwigType_add_pointer(type);
            Parm *parm = NewParm(type, "self", n);
            Setattr(n, "wrap:parms", parm);

            String *pn = Swig_cparm_name(parm, 0);
            String *action = NewString("");
            Printv(action, Swig_cresult_name(), " = (", Getattr(b.item, "classtype"), "*)", pn, ";", NULL);
            Delete(pn);

            Setattr(n, "wrap:action", action);

            String *name = Copy(class_name);
            Append(name, "_SwigGet");
            Append(name, go_base_name);

            String *go_name = NewString("SwigGet");
            String *c1 = exportedName(go_base_name);
            Append(go_name, c1);
            Delete(c1);

            String *wname = Swig_name_wrapper(name);
            Append(wname, unique_id);
            Setattr(n, "wrap:name", wname);

            SwigType *result = Copy(Getattr(b.item, "classtypeobj"));
            SwigType_add_pointer(result);

            int r = makeWrappers(n, go_name, NULL, wname, NULL, parm, result, false);
            if (r != SWIG_OK) {
                return r;
            }

            Swig_restore(n);

            Setattr(parents, go_base_name, NewString(""));

            Delete(go_name);
            Delete(type);
            Delete(parm);
            Delete(action);
            Delete(result);

            String *ns = NewString("");
            addParentExtraBaseInterfaces(n, parents, b.item, false, ns);
            Delete(ns);
        }

        if (!GetFlag(fb, "feature:ignore")) {
            String *ns = NewString("");
            addParentExtraBaseInterfaces(n, parents, fb, true, ns);
            Delete(ns);
        }

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * addParentExtraBaseInterfaces()
   *
   * Add functions to retrieve the base class pointers for all base
   * classes of parents other than the first base class at each level.
   * ------------------------------------------------------------ */

    void addParentExtraBaseInterfaces(Node *n, Hash *parents, Node *base, bool is_base_first, String *sofar) {
        List *baselist = Getattr(base, "bases");
        if (!baselist || Len(baselist) == 0) {
            return;
        }

        String *go_this_base_name = exportedName(Getattr(base, "sym:name"));

        String *sf = NewString("");
        Printv(sf, sofar, ".SwigGet", go_this_base_name, "()", NULL);

        Iterator b = First(baselist);

        if (is_base_first) {
            if (!b.item) {
                return;
            }
            if (!GetFlag(b.item, "feature:ignore")) {
                addParentExtraBaseInterfaces(n, parents, b.item, true, sf);
            }

            b = Next(b);
        }

        String *go_name = buildGoName(Getattr(n, "sym:name"), false, false);
        String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);

        for (; b.item; b = Next(b)) {
            if (GetFlag(b.item, "feature:ignore")) {
                continue;
            }

            String *go_base_name = exportedName(Getattr(b.item, "sym:name"));

            if (!Getattr(parents, go_base_name)) {
                Printv(f_go_wrappers, "func (p ", go_type_name, ") SwigGet", go_base_name, "() ", go_base_name, " {\n",
                       NULL);
                Printv(f_go_wrappers, "\treturn p", sf, ".SwigGet", go_base_name, "()\n", NULL);
                Printv(f_go_wrappers, "}\n\n", NULL);

                Printv(interfaces, "\tSwigGet", go_base_name, "() ", go_base_name, "\n", NULL);

                addParentExtraBaseInterfaces(n, parents, b.item, false, sf);

                Setattr(parents, go_base_name, NewString(""));
            }
        }

        Delete(go_name);
        Delete(go_type_name);
        Delete(go_this_base_name);
        Delete(sf);
    }

    /* ------------------------------------------------------------
   * classDirectorInit
   *
   * Add support for a director class.
   *
   * Virtual inheritance is different in Go and C++.  We implement
   * director classes by defining a new function in Go,
   * NewDirectorClassname, which takes a empty interface value and
   * creates an instance of a new child class.  The new child class
   * refers all methods back to Go.  The Go code checks whether the
   * value passed to NewDirectorClassname implements that method; if
   * it does, it calls it, otherwise it calls back into C++.
   * ------------------------------------------------------------ */

    int classDirectorInit(Node *n) {
        // Because we use a different function to handle inheritance in
        // Go, ordinary creations of the object should not create a
        // director object.
        Delete(director_ctor_code);
        director_ctor_code = NewString("$nondirector_new");

        class_node = n;

        String *name = Getattr(n, "sym:name");

        assert(!class_name);
        class_name = name;

        String *go_name = exportedName(name);

        String *go_type_name = goCPointerType(Getattr(n, "classtypeobj"), true);

        assert(!class_receiver);
        class_receiver = go_type_name;

        String *director_struct_name = NewString("_swig_Director");
        Append(director_struct_name, go_name);

        String *cxx_director_name = NewString("SwigDirector_");
        Append(cxx_director_name, name);

        // The Go type of the director class.
        Printv(f_go_wrappers, "type ", director_struct_name, " struct {\n", NULL);
        Printv(f_go_wrappers, "\t", go_type_name, "\n", NULL);
        Printv(f_go_wrappers, "\tv interface{}\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        Printv(f_go_wrappers, "func (p *", director_struct_name, ") Swigcptr() uintptr {\n", NULL);
        Printv(f_go_wrappers, "\treturn getSwigcptr(p.", go_type_name, ")\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        Printv(f_go_wrappers, "func (p *", director_struct_name, ") SwigIs", go_name, "() {\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        Printv(f_go_wrappers, "func (p *", director_struct_name, ") DirectorInterface() interface{} {\n", NULL);
        Printv(f_go_wrappers, "\treturn p.v\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        // Start defining the director class.
        Printv(f_c_directors_h, "class ", cxx_director_name, " : public ", Getattr(n, "classtype"), "\n", NULL);
        Printv(f_c_directors_h, "{\n", NULL);
        Printv(f_c_directors_h, " public:\n", NULL);

        Delete(director_struct_name);
        Delete(cxx_director_name);

        class_methods = NewHash();

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * classDirectorConstructor
   *
   * Emit a constructor for a director class.
   * ------------------------------------------------------------ */

    int classDirectorConstructor(Node *n) {
        bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;

        String *name = Getattr(n, "sym:name");
        if (!name) {
            assert(is_ignored);
            name = Getattr(n, "name");
        }

        String *overname = NULL;
        if (Getattr(n, "sym:overloaded")) {
            overname = Getattr(n, "sym:overname");
        }

        String *go_name = exportedName(name);

        ParmList *parms = Getattr(n, "parms");
        Setattr(n, "wrap:parms", parms);

        String *cn = exportedName(Getattr(parentNode(n), "sym:name"));

        String *go_type_name = goCPointerType(Getattr(parentNode(n), "classtypeobj"), true);

        String *director_struct_name = NewString("_swig_Director");
        Append(director_struct_name, cn);

        String *fn_name = NewString("_swig_NewDirector");
        Append(fn_name, cn);
        Append(fn_name, go_name);

        if (!overname && !is_ignored) {
            if (!checkNameConflict(fn_name, n, NULL)) {
                return SWIG_NOWRAP;
            }
        }

        String *fn_with_over_name = Copy(fn_name);
        if (overname) {
            Append(fn_with_over_name, overname);
        }

        String *wname = Swig_name_wrapper(fn_name);

        if (overname) {
            Append(wname, overname);
        }
        Append(wname, unique_id);
        Setattr(n, "wrap:name", wname);

        bool is_static = isStatic(n);

        Wrapper *dummy = NewWrapper();
        emit_attach_parmmaps(parms, dummy);
        DelWrapper(dummy);

        Swig_typemap_attach_parms("gotype", parms, NULL);
        Swig_typemap_attach_parms("goin", parms, NULL);
        Swig_typemap_attach_parms("goargout", parms, NULL);
        Swig_typemap_attach_parms("imtype", parms, NULL);
        int parm_count = emit_num_arguments(parms);

        String *func_name = NewString("NewDirector");
        Append(func_name, go_name);

        String *func_with_over_name = Copy(func_name);
        if (overname) {
            Append(func_with_over_name, overname);
        }

        SwigType *first_type = NewString("int");
        Parm *first_parm = NewParm(first_type, "swig_p", n);
        set_nextSibling(first_parm, parms);
        Setattr(first_parm, "lname", "p");

        Parm *p = parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            Swig_cparm_name(p, i);
            p = nextParm(p);
        }

        if (!is_ignored) {
            Printv(f_cgo_comment, "extern uintptr_t ", wname, "(int", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                bool c_struct_type;
                String *ct = cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type);
                Printv(f_cgo_comment, ", ", ct, " ", Getattr(p, "lname"), NULL);
                p = nextParm(p);
            }
            Printv(f_cgo_comment, ");\n", NULL);

            // Write out the Go function that calls the wrapper.

            Printv(f_go_wrappers, "func ", func_with_over_name, "(v interface{}", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                Printv(f_go_wrappers, ", ", Getattr(p, "lname"), " ", NULL);
                String *tm = goType(p, Getattr(p, "type"));
                Printv(f_go_wrappers, tm, NULL);
                Delete(tm);
                p = nextParm(p);
            }

            Printv(f_go_wrappers, ") ", cn, " {\n", NULL);

            Printv(f_go_wrappers, "\tp := &", director_struct_name, "{0, v}\n", NULL);

            String *call = NewString("");

            Printv(call, "\tp.", class_receiver, " = ", NULL);
            Printv(call, go_type_name, "(C.", wname, "(C.int(swigDirectorAdd(p))", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                Printv(call, ", ", NULL);

                p = getParm(p);
                String *pt = Getattr(p, "type");
                String *ln = Getattr(p, "lname");

                String *ivar = NewStringf("_swig_i_%d", i);

                String *goin = goGetattr(p, "tmap:goin");
                if (goin == NULL) {
                    Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
                    bool need_close = false;
                    if (goTypeIsInterface(p, pt)) {
                        Printv(f_go_wrappers, "getSwigcptr(", NULL);
                        need_close = true;
                    }
                    Printv(f_go_wrappers, ln, NULL);
                    if (need_close) {
                        Printv(f_go_wrappers, ")", NULL);
                    }
                    Printv(f_go_wrappers, "\n", NULL);
                } else {
                    String *itm = goImType(p, pt);
                    Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
                    goin = Copy(goin);
                    Replaceall(goin, "$input", ln);
                    Replaceall(goin, "$result", ivar);
                    Printv(f_go_wrappers, goin, "\n", NULL);
                    Delete(goin);
                }

                Setattr(p, "emit:goinput", ivar);

                bool c_struct_type;
                String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
                if (c_struct_type) {
                    Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
                } else {
                    Printv(call, "C.", ct, "(", ivar, ")", NULL);
                }
                Delete(ct);

                p = nextParm(p);
            }

            Printv(call, "))", NULL);

            Printv(f_go_wrappers, call, "\n", NULL);

            goargout(parms);

            Printv(f_go_wrappers, "\treturn p\n", NULL);
            Printv(f_go_wrappers, "}\n\n", NULL);

            SwigType *result = Copy(Getattr(parentNode(n), "classtypeobj"));
            SwigType_add_pointer(result);

            Swig_save("classDirectorConstructor", n, "wrap:name", "wrap:action", NULL);

            String *dwname = Swig_name_wrapper(name);
            Append(dwname, unique_id);
            Setattr(n, "wrap:name", dwname);

            String *action = NewString("");
            Printv(action, Swig_cresult_name(), " = new SwigDirector_", class_name, "(", NULL);
            String *pname = Swig_cparm_name(NULL, 0);
            Printv(action, pname, NULL);
            Delete(pname);
            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                String *pname = Swig_cparm_name(NULL, i + 1);
                Printv(action, ", ", NULL);
                if (SwigType_isreference(Getattr(p, "type"))) {
                    Printv(action, "*", NULL);
                }
                Printv(action, pname, NULL);
                Delete(pname);
                p = nextParm(p);
            }
            Printv(action, ");", NULL);
            Setattr(n, "wrap:action", action);

            cgoWrapperInfo info;

            info.n = n;
            info.go_name = func_name;
            info.overname = overname;
            info.wname = wname;
            info.base = NULL;
            info.parms = first_parm;
            info.result = result;
            info.is_static = false;
            info.receiver = NULL;
            info.is_constructor = true;
            info.is_destructor = false;

            int r = cgoGccWrapper(&info);
            if (r != SWIG_OK) {
                return r;
            }

            Swig_restore(n);

            Delete(result);
        }

        String *cxx_director_name = NewString("SwigDirector_");
        Append(cxx_director_name, class_name);

        String *decl = Swig_method_decl(NULL, Getattr(n, "decl"), cxx_director_name, first_parm, 0);
        Printv(f_c_directors_h, "  ", decl, ";\n", NULL);
        Delete(decl);

        decl = Swig_method_decl(NULL, Getattr(n, "decl"), cxx_director_name, first_parm, 0);
        Printv(f_c_directors, cxx_director_name, "::", decl, "\n", NULL);
        Delete(decl);

        Printv(f_c_directors, "    : ", Getattr(parentNode(n), "classtype"), "(", NULL);

        p = parms;
        for (int i = 0; i < parm_count; ++i) {
            p = getParm(p);
            if (i > 0) {
                Printv(f_c_directors, ", ", NULL);
            }
            String *pn = Getattr(p, "name");
            assert(pn);
            Printv(f_c_directors, pn, NULL);
            p = nextParm(p);
        }
        Printv(f_c_directors, "),\n", NULL);
        Printv(f_c_directors, "      go_val(swig_p), swig_mem(0)\n", NULL);
        Printv(f_c_directors, "{ }\n\n", NULL);

        if (Getattr(n, "sym:overloaded") && !Getattr(n, "sym:nextSibling")) {
            int r = makeDispatchFunction(n, func_name, cn, is_static, Getattr(parentNode(n), "classtypeobj"), false);
            if (r != SWIG_OK) {
                return r;
            }
        }

        Delete(cxx_director_name);
        Delete(go_name);
        Delete(cn);
        Delete(go_type_name);
        Delete(director_struct_name);
        Delete(fn_name);
        Delete(fn_with_over_name);
        Delete(func_name);
        Delete(func_with_over_name);
        Delete(wname);
        Delete(first_type);
        Delete(first_parm);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * classDirectorDestructor
   *
   * Emit a destructor for a director class.
   * ------------------------------------------------------------ */

    int classDirectorDestructor(Node *n) {
        if (!is_public(n)) {
            return SWIG_OK;
        }

        bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;

        if (!is_ignored) {
            String *fnname = NewString("DeleteDirector");
            String *c1 = exportedName(class_name);
            Append(fnname, c1);
            Delete(c1);

            String *wname = Swig_name_wrapper(fnname);
            Append(wname, unique_id);

            Setattr(n, "wrap:name", fnname);

            Swig_DestructorToFunction(n, getNSpace(), getClassType(), CPlusPlus, Extend);

            ParmList *parms = Getattr(n, "parms");
            Setattr(n, "wrap:parms", parms);

            String *result = NewString("void");
            int r = makeWrappers(n, fnname, NULL, wname, NULL, parms, result, isStatic(n));
            if (r != SWIG_OK) {
                return r;
            }

            Delete(result);
            Delete(fnname);
            Delete(wname);
        }

        // Generate the destructor for the C++ director class.  Since the
        // Go code is keeping a pointer to the C++ object, we need to call
        // back to the Go code to let it know that the C++ object is gone.

        String *go_name = NewString("Swiggo_DeleteDirector_");
        Append(go_name, class_name);

        String *cn = exportedName(class_name);

        String *director_struct_name = NewString("_swig_Director");
        Append(director_struct_name, cn);

        Printv(f_c_directors_h, "  virtual ~SwigDirector_", class_name, "()", NULL);

        String *throws = buildThrow(n);
        if (throws) {
            Printv(f_c_directors_h, " ", throws, NULL);
        }

        Printv(f_c_directors_h, ";\n", NULL);

        String *director_sig = NewString("");

        Printv(director_sig, "SwigDirector_", class_name, "::~SwigDirector_", class_name, "()", NULL);

        if (throws) {
            Printv(director_sig, " ", throws, NULL);
            Delete(throws);
        }

        Printv(director_sig, "\n", NULL);
        Printv(director_sig, "{\n", NULL);

        if (is_ignored) {
            Printv(f_c_directors, director_sig, NULL);
        } else {
            makeDirectorDestructorWrapper(go_name, director_struct_name, director_sig);
        }

        Printv(f_c_directors, "  delete swig_mem;\n", NULL);

        Printv(f_c_directors, "}\n\n", NULL);

        Delete(director_sig);
        Delete(go_name);
        Delete(cn);
        Delete(director_struct_name);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * makeDirectorDestructorWrapper
   *
   * Emit the function wrapper for the destructor of a director class.
   * ------------------------------------------------------------ */

    void makeDirectorDestructorWrapper(String *go_name, String *director_struct_name, String *director_sig) {
        String *wname = Copy(go_name);
        Append(wname, unique_id);

        Printv(f_go_wrappers, "//export ", wname, "\n", NULL);
        Printv(f_go_wrappers, "func ", wname, "(c int) {\n", NULL);
        Printv(f_go_wrappers, "\tswigDirectorLookup(c).(*", director_struct_name, ").", class_receiver, " = 0\n", NULL);
        Printv(f_go_wrappers, "\tswigDirectorDelete(c)\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        Printv(f_c_directors, "extern \"C\" void ", wname, "(intgo);\n", NULL);
        Printv(f_c_directors, director_sig, NULL);
        Printv(f_c_directors, "  ", wname, "(go_val);\n", NULL);
    }

    /* ------------------------------------------------------------
   * classDirectorMethod
   *
   * Emit a method for a director class, plus its overloads.
   * ------------------------------------------------------------ */

    int classDirectorMethod(Node *n, Node *parent, String *super) {
        bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;

        // We don't need explicit calls.
        if (GetFlag(n, "explicitcall")) {
            return SWIG_OK;
        }

        String *name = Getattr(n, "sym:name");
        if (!name) {
            assert(is_ignored);
            (void) is_ignored;
            name = Getattr(n, "name");
        }

        bool overloaded = Getattr(n, "sym:overloaded") && !Getattr(n, "explicitcallnode");
        if (!overloaded) {
            int r = oneClassDirectorMethod(n, parent, super);
            if (r != SWIG_OK) {
                return r;
            }
        } else {
            // Handle overloaded methods here, because otherwise we will
            // reject them in the class_methods hash table.  We need to use
            // class_methods so that we correctly handle cases where a
            // function in one class hides a function of the same name in a
            // parent class.
            if (!Getattr(class_methods, name)) {
                for (Node *on = Getattr(n, "sym:overloaded"); on; on = Getattr(on, "sym:nextSibling")) {
                    // Swig_overload_rank expects wrap:name and wrap:parms to be
                    // set.
                    String *wn = Swig_name_wrapper(Getattr(on, "sym:name"));
                    Append(wn, Getattr(on, "sym:overname"));
                    Append(wn, unique_id);
                    Setattr(on, "wrap:name", wn);
                    Delete(wn);
                    Setattr(on, "wrap:parms", Getattr(on, "parms"));
                }
            }

            int r = oneClassDirectorMethod(n, parent, super);
            if (r != SWIG_OK) {
                return r;
            }

            if (!Getattr(n, "sym:nextSibling")) {
                // Last overloaded function
                Node *on = Getattr(n, "sym:overloaded");
                bool is_static = isStatic(on);

                String *cn = exportedName(Getattr(parent, "sym:name"));
                String *go_name = buildGoName(name, is_static, false);

                String *director_struct_name = NewString("_swig_Director");
                Append(director_struct_name, cn);

                int r = makeDispatchFunction(on, go_name, director_struct_name, is_static, director_struct_name, false);
                if (r != SWIG_OK) {
                    return r;
                }

                if (!GetFlag(n, "abstract")) {
                    String *go_upcall = NewString("Director");
                    Append(go_upcall, cn);
                    Append(go_upcall, go_name);
                    r = makeDispatchFunction(on, go_upcall, director_struct_name, is_static, director_struct_name,
                                             true);
                    if (r != SWIG_OK) {
                        return r;
                    }
                    Delete(go_upcall);
                }

                Delete(director_struct_name);
                Delete(go_name);
                Delete(cn);
            }
        }
        Setattr(class_methods, name, NewString(""));

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * oneClassDirectorMethod
   *
   * Emit a method for a director class.
   * ------------------------------------------------------------ */

    int oneClassDirectorMethod(Node *n, Node *parent, String *super) {
        String *symname = Getattr(n, "sym:name");
        if (!checkFunctionVisibility(n, parent)) {
            return SWIG_OK;
        }

        bool is_ignored = GetFlag(n, "feature:ignore") ? true : false;
        bool is_pure_virtual = (Cmp(Getattr(n, "storage"), "virtual") == 0 && Cmp(Getattr(n, "value"), "0") == 0);

        String *name = Getattr(n, "sym:name");
        if (!name) {
            assert(is_ignored);
            name = Getattr(n, "name");
        }

        String *overname = NULL;
        if (Getattr(n, "sym:overloaded")) {
            overname = Getattr(n, "sym:overname");
        }

        String *cn = exportedName(Getattr(parent, "sym:name"));

        String *go_type_name = goCPointerType(Getattr(parent, "classtypeobj"), true);

        String *director_struct_name = NewString("_swig_Director");
        Append(director_struct_name, cn);

        bool is_static = isStatic(n);

        String *go_name = buildGoName(name, is_static, false);

        ParmList *parms = Getattr(n, "parms");
        Setattr(n, "wrap:parms", parms);

        Wrapper *dummy = NewWrapper();
        emit_attach_parmmaps(parms, dummy);

        Swig_typemap_attach_parms("gotype", parms, NULL);
        Swig_typemap_attach_parms("imtype", parms, NULL);
        int parm_count = emit_num_arguments(parms);

        SwigType *result = Getattr(n, "type");

        // Save the type for overload processing.
        Setattr(n, "go:type", result);

        String *interface_name = NewString("_swig_DirectorInterface");
        Append(interface_name, cn);
        Append(interface_name, go_name);
        if (overname) {
            Append(interface_name, overname);
        }

        String *callback_name = Copy(director_struct_name);
        Append(callback_name, "_callback_");
        Append(callback_name, name);
        Replace(callback_name, "_swig", "Swig", DOH_REPLACE_FIRST);
        if (overname) {
            Append(callback_name, overname);
        }
        Append(callback_name, unique_id);

        String *upcall_name = Copy(director_struct_name);
        Append(upcall_name, "_upcall_");
        Append(upcall_name, go_name);

        String *upcall_wname = Swig_name_wrapper(upcall_name);
        if (overname) {
            Append(upcall_wname, overname);
        }
        Append(upcall_wname, unique_id);

        String *upcall_gc_name = buildGoWrapperName(upcall_name, overname);

        String *go_with_over_name = Copy(go_name);
        if (overname) {
            Append(go_with_over_name, overname);
        }

        Parm *p = 0;
        Wrapper *w = NewWrapper();

        Swig_director_parms_fixup(parms);

        Swig_typemap_attach_parms("directorin", parms, w);
        Swig_typemap_attach_parms("directorargout", parms, w);
        Swig_typemap_attach_parms("godirectorin", parms, w);
        Swig_typemap_attach_parms("goin", parms, dummy);
        Swig_typemap_attach_parms("goargout", parms, dummy);

        DelWrapper(dummy);

        if (!is_ignored) {
            // We use an interface to see if this method is defined in Go.
            Printv(f_go_wrappers, "type ", interface_name, " interface {\n", NULL);
            Printv(f_go_wrappers, "\t", go_with_over_name, "(", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                if (i > 0) {
                    Printv(f_go_wrappers, ", ", NULL);
                }
                String *tm = goType(p, Getattr(p, "type"));
                Printv(f_go_wrappers, tm, NULL);
                Delete(tm);
                p = nextParm(p);
            }

            Printv(f_go_wrappers, ")", NULL);

            if (SwigType_type(result) != T_VOID) {
                String *tm = goType(n, result);
                Printv(f_go_wrappers, " ", tm, NULL);
                Delete(tm);
            }

            Printv(f_go_wrappers, "\n", NULL);
            Printv(f_go_wrappers, "}\n\n", NULL);

            if (!GetFlag(n, "abstract")) {
                Printv(f_cgo_comment, "extern ", NULL);

                if (SwigType_type(result) == T_VOID) {
                    Printv(f_cgo_comment, "void", NULL);
                } else {
                    bool c_struct_type;
                    String *ret_type = cgoTypeForGoValue(n, result, &c_struct_type);
                    Printv(f_cgo_comment, ret_type, NULL);
                    Delete(ret_type);
                }

                Printv(f_cgo_comment, " ", upcall_wname, "(uintptr_t", NULL);

                p = parms;
                for (int i = 0; i < parm_count; ++i) {
                    p = getParm(p);
                    bool c_struct_type;
                    String *ct = cgoTypeForGoValue(p, Getattr(p, "type"), &c_struct_type);
                    Printv(f_cgo_comment, ", ", ct, " ", Getattr(p, "lname"), NULL);
                    p = nextParm(p);
                }
                Printv(f_cgo_comment, ");\n", NULL);
            }

            // Define the method on the director class in Go.

            Printv(f_go_wrappers, "func (swig_p *", director_struct_name, ") ", go_with_over_name, "(", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                if (i > 0) {
                    Printv(f_go_wrappers, ", ", NULL);
                }
                Printv(f_go_wrappers, Getattr(p, "lname"), " ", NULL);
                String *tm = goType(p, Getattr(p, "type"));
                Printv(f_go_wrappers, tm, NULL);
                Delete(tm);
                p = nextParm(p);
            }

            Printv(f_go_wrappers, ")", NULL);

            if (SwigType_type(result) != T_VOID) {
                String *tm = goType(n, result);
                Printv(f_go_wrappers, " ", tm, NULL);
                Delete(tm);
            }

            Printv(f_go_wrappers, " {\n", NULL);

            Printv(f_go_wrappers, "\tif swig_g, swig_ok := swig_p.v.(", interface_name, "); swig_ok {\n", NULL);
            Printv(f_go_wrappers, "\t\t", NULL);
            if (SwigType_type(result) != T_VOID) {
                Printv(f_go_wrappers, "return ", NULL);
            }
            Printv(f_go_wrappers, "swig_g.", go_with_over_name, "(", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                if (i > 0) {
                    Printv(f_go_wrappers, ", ", NULL);
                }
                Printv(f_go_wrappers, Getattr(p, "lname"), NULL);
                p = nextParm(p);
            }

            Printv(f_go_wrappers, ")\n", NULL);
            if (SwigType_type(result) == T_VOID) {
                Printv(f_go_wrappers, "\t\treturn\n", NULL);
            }
            Printv(f_go_wrappers, "\t}\n", NULL);

            if (GetFlag(n, "abstract")) {
                Printv(f_go_wrappers, "\tpanic(\"call to pure virtual method\")\n", NULL);
            } else {
                String *ret_type = NULL;
                bool memcpy_ret = false;
                String *wt = NULL;
                String *goout = NULL;
                if (SwigType_type(result) != T_VOID) {
                    ret_type = goImType(n, result);
                    Printv(f_go_wrappers, "\tvar swig_r ", ret_type, "\n", NULL);
                    goout = goTypemapLookup("goout", n, "swig_r");

                    bool c_struct_type;
                    Delete(cgoTypeForGoValue(n, result, &c_struct_type));
                    if (c_struct_type) {
                        memcpy_ret = true;
                    }
                }

                String *call = NewString("");

                Printv(call, "\t", NULL);
                if (SwigType_type(result) != T_VOID) {
                    if (memcpy_ret) {
                        Printv(call, "swig_r_p := ", NULL);
                    } else {
                        Printv(call, "swig_r = (", ret_type, ")(", NULL);
                    }
                    if (goTypeIsInterface(n, result)) {
                        wt = goWrapperType(n, result, true);
                        Printv(call, "(", wt, ")(", NULL);
                    }
                }

                Printv(call, "C.", upcall_wname, "(C.uintptr_t(swig_p.",
                       go_type_name, ")", NULL);

                p = parms;
                for (int i = 0; i < parm_count; ++i) {
                    Printv(call, ", ", NULL);
                    p = getParm(p);
                    SwigType *pt = Getattr(p, "type");

                    String *ln = Getattr(p, "lname");

                    String *ivar = NewStringf("_swig_i_%d", i);

                    // This is an ordinary call from Go to C++, so adjust using
                    // the goin typemap.
                    String *goin = goGetattr(p, "tmap:goin");
                    if (goin == NULL) {
                        Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
                        bool need_close = false;
                        if (goTypeIsInterface(p, pt)) {
                            Printv(f_go_wrappers, "getSwigcptr(", NULL);
                            need_close = true;
                        }
                        Printv(f_go_wrappers, ln, NULL);
                        if (need_close) {
                            Printv(f_go_wrappers, ")", NULL);
                        }
                        Printv(f_go_wrappers, "\n", NULL);
                    } else {
                        String *itm = goImType(p, pt);
                        Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
                        goin = Copy(goin);
                        Replaceall(goin, "$input", ln);
                        Replaceall(goin, "$result", ivar);
                        Printv(f_go_wrappers, goin, "\n", NULL);
                        Delete(goin);
                    }

                    Setattr(p, "emit:goinput", ivar);

                    bool c_struct_type;
                    String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
                    if (c_struct_type) {
                        Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
                    } else {
                        Printv(call, "C.", ct, "(", ivar, ")", NULL);
                    }

                    p = nextParm(p);
                }

                Printv(call, ")", NULL);

                if (wt) {
                    // Close the type conversion to the wrapper type.
                    Printv(call, ")", NULL);
                }
                if (SwigType_type(result) != T_VOID && !memcpy_ret) {
                    // Close the type conversion of the return value.
                    Printv(call, ")", NULL);
                }

                Printv(call, "\n", NULL);

                Printv(f_go_wrappers, call, NULL);
                Delete(call);

                if (memcpy_ret) {
                    Printv(f_go_wrappers, "\tswig_r = *(*", ret_type, ")(unsafe.Pointer(&swig_r_p))\n", NULL);
                }

                goargout(parms);

                if (SwigType_type(result) != T_VOID) {
                    if (goout == NULL) {
                        Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
                    } else {
                        String *tm = goType(n, result);
                        Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
                        Replaceall(goout, "$input", "swig_r");
                        Replaceall(goout, "$result", "swig_r_1");
                        Printv(f_go_wrappers, goout, "\n", NULL);
                        Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
                    }
                }

                if (ret_type) {
                    Delete(ret_type);
                }
                if (wt) {
                    Delete(wt);
                }
            }

            Printv(f_go_wrappers, "}\n\n", NULL);

            if (!GetFlag(n, "abstract")) {
                // Define a function that uses the Go director type that other
                // methods in the Go type can call to get parent methods.

                Printv(f_go_wrappers, "func Director", cn, go_with_over_name, "(swig_p ", cn, NULL);

                p = parms;
                for (int i = 0; i < parm_count; ++i) {
                    p = getParm(p);
                    Printv(f_go_wrappers, ", ", Getattr(p, "lname"), " ", NULL);
                    String *tm = goType(p, Getattr(p, "type"));
                    Printv(f_go_wrappers, tm, NULL);
                    Delete(tm);
                    p = nextParm(p);
                }

                Printv(f_go_wrappers, ")", NULL);

                if (SwigType_type(result) != T_VOID) {
                    String *tm = goType(n, result);
                    Printv(f_go_wrappers, " ", tm, NULL);
                    Delete(tm);
                }

                Printv(f_go_wrappers, " {\n", NULL);

                String *ret_type = NULL;
                bool memcpy_ret = false;
                String *wt = NULL;
                String *goout = NULL;
                if (SwigType_type(result) != T_VOID) {
                    ret_type = goImType(n, result);
                    Printv(f_go_wrappers, "\tvar swig_r ", ret_type, "\n", NULL);
                    goout = goTypemapLookup("goout", n, "swig_r");

                    bool c_struct_type;
                    Delete(cgoTypeForGoValue(n, result, &c_struct_type));
                    if (c_struct_type) {
                        memcpy_ret = true;
                    }
                }

                String *call = NewString("");

                Printv(call, "\t", NULL);
                if (SwigType_type(result) != T_VOID) {
                    if (memcpy_ret) {
                        Printv(call, "swig_r_p := ", NULL);
                    } else {
                        Printv(call, "swig_r = (", ret_type, ")(", NULL);
                    }
                    if (goTypeIsInterface(n, result)) {
                        wt = goWrapperType(n, result, true);
                        Printv(call, "(", wt, ")(", NULL);
                    }
                }

                Printv(call, "C.", upcall_wname, "(C.uintptr_t(swig_p.(*",
                       director_struct_name, ").", go_type_name, ")", NULL);

                p = parms;
                for (int i = 0; i < parm_count; ++i) {
                    Printv(call, ", ", NULL);
                    p = getParm(p);
                    SwigType *pt = Getattr(p, "type");

                    String *ivar = NewStringf("_swig_i_%d", i);

                    String *ln = Copy(Getattr(p, "lname"));

                    String *goin = goGetattr(p, "tmap:goin");
                    if (goin == NULL) {
                        Printv(f_go_wrappers, "\t", ivar, " := ", NULL);
                        bool need_close = false;
                        if (goTypeIsInterface(p, pt)) {
                            Printv(f_go_wrappers, "getSwigcptr(", NULL);
                            need_close = true;
                        }
                        Printv(f_go_wrappers, ln, NULL);
                        if (need_close) {
                            Printv(f_go_wrappers, ")", NULL);
                        }
                        Printv(f_go_wrappers, "\n", NULL);
                    } else {
                        String *itm = goImType(p, pt);
                        Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
                        goin = Copy(goin);
                        Replaceall(goin, "$input", ln);
                        Replaceall(goin, "$result", ivar);
                        Printv(f_go_wrappers, goin, "\n", NULL);
                        Delete(goin);
                    }

                    Setattr(p, "emit:goinput", ivar);

                    bool c_struct_type;
                    String *ct = cgoTypeForGoValue(p, pt, &c_struct_type);
                    if (c_struct_type) {
                        Printv(call, "*(*C.", ct, ")(unsafe.Pointer(&", ivar, "))", NULL);
                    } else {
                        Printv(call, "C.", ct, "(", ivar, ")", NULL);
                    }

                    Delete(ln);

                    p = nextParm(p);
                }

                Printv(call, ")", NULL);

                if (wt) {
                    // Close the type conversion to the wrapper type.
                    Printv(call, ")", NULL);
                }
                if (SwigType_type(result) != T_VOID && !memcpy_ret) {
                    // Close the type conversion of the return value.
                    Printv(call, ")", NULL);
                }

                Printv(call, "\n", NULL);

                Printv(f_go_wrappers, call, NULL);
                Delete(call);

                if (memcpy_ret) {
                    Printv(f_go_wrappers, "\tswig_r = *(*", ret_type, ")(unsafe.Pointer(&swig_r_p))\n", NULL);
                }

                goargout(parms);

                if (SwigType_type(result) != T_VOID) {
                    if (goout == NULL) {
                        Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
                    } else {
                        String *tm = goType(n, result);
                        Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
                        Replaceall(goout, "$input", "swig_r");
                        Replaceall(goout, "$result", "swig_r_1");
                        Printv(f_go_wrappers, goout, "\n", NULL);
                        Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
                    }
                }

                Printv(f_go_wrappers, "}\n\n", NULL);

                if (ret_type) {
                    Delete(ret_type);
                }
                if (wt) {
                    Delete(wt);
                }

                // Define a method in the C++ director class that the C++
                // upcall function can call.  This permits an upcall to a
                // protected method.

                String *upcall_method_name = NewString("_swig_upcall_");
                Append(upcall_method_name, name);
                if (overname) {
                    Append(upcall_method_name, overname);
                }
                SwigType *rtype = Getattr(n, "classDirectorMethods:type");
                String *upcall_decl = Swig_method_decl(rtype, Getattr(n, "decl"), upcall_method_name, parms, 0);
                Printv(f_c_directors_h, "  ", upcall_decl, " {\n", NULL);
                Delete(upcall_decl);

                Printv(f_c_directors_h, "    ", NULL);
                if (SwigType_type(result) != T_VOID) {
                    Printv(f_c_directors_h, "return ", NULL);
                }

                String *super_call = Swig_method_call(super, parms);
                Printv(f_c_directors_h, super_call, ";\n", NULL);
                Delete(super_call);

                Printv(f_c_directors_h, "  }\n", NULL);

                // Define the C++ function that the Go function calls.

                SwigType *first_type = NULL;
                Parm *first_parm = parms;
                if (!is_static) {
                    first_type = NewString("SwigDirector_");
                    Append(first_type, class_name);
                    SwigType_add_pointer(first_type);
                    first_parm = NewParm(first_type, "p", n);
                    set_nextSibling(first_parm, parms);
                }

                Swig_save("classDirectorMethod", n, "wrap:name", "wrap:action", NULL);

                Setattr(n, "wrap:name", upcall_wname);

                String *action = NewString("");
                if (SwigType_type(result) != T_VOID) {
                    Printv(action, Swig_cresult_name(), " = (", SwigType_lstr(result, 0), ")", NULL);
                    if (SwigType_isreference(result)) {
                        Printv(action, "&", NULL);
                    }
                }
                Printv(action, Swig_cparm_name(NULL, 0), "->", upcall_method_name, "(", NULL);

                p = parms;
                int i = 0;
                while (p != NULL) {
                    if (SwigType_type(Getattr(p, "type")) != T_VOID) {
                        String *pname = Swig_cparm_name(NULL, i + 1);
                        if (i > 0) {
                            Printv(action, ", ", NULL);
                        }

                        // A parameter whose type is a reference is converted into a
                        // pointer type by gcCTypeForGoValue.  We are calling a
                        // function which expects a reference so we need to convert
                        // back.
                        if (SwigType_isreference(Getattr(p, "type"))) {
                            Printv(action, "*", NULL);
                        }

                        Printv(action, pname, NULL);
                        Delete(pname);
                        i++;
                    }
                    p = nextSibling(p);
                }
                Printv(action, ");", NULL);
                Setattr(n, "wrap:action", action);

                cgoWrapperInfo info;

                info.n = n;
                info.go_name = go_name;
                info.overname = overname;
                info.wname = upcall_wname;
                info.base = NULL;
                info.parms = first_parm;
                info.result = result;
                info.is_static = is_static;
                info.receiver = NULL;
                info.is_constructor = false;
                info.is_destructor = false;

                int r = cgoGccWrapper(&info);
                if (r != SWIG_OK) {
                    return r;
                }

                Delete(first_type);
                if (first_parm != parms) {
                    Delete(first_parm);
                }

                Swig_restore(n);
                Delete(upcall_method_name);
            }

            // The Go function which invokes the method.  This is called by
            // the C++ method on the director class.

            Printv(f_go_wrappers, "//export ", callback_name, "\n",
                   "func ", callback_name, "(swig_c int", NULL);

            p = parms;
            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                String *tm = goWrapperType(p, Getattr(p, "type"), false);
                Printv(f_go_wrappers, ", ", Getattr(p, "lname"), " ", tm, NULL);
                Delete(tm);
                p = nextParm(p);
            }

            Printv(f_go_wrappers, ") ", NULL);
            String *result_wrapper = NULL;
            if (SwigType_type(result) != T_VOID) {
                result_wrapper = goWrapperType(n, result, true);
                Printv(f_go_wrappers, "(swig_result ", result_wrapper, ") ", NULL);
            }
            Printv(f_go_wrappers, "{\n", NULL);

            if (is_ignored) {
                Printv(f_go_wrappers, "\treturn\n", NULL);
            } else {
                bool result_is_interface = false;
                String *goout = NULL;
                if (SwigType_type(result) != T_VOID) {
                    result_is_interface = goTypeIsInterface(NULL, result);
                    Printv(f_go_wrappers, "\tvar swig_r ", NULL);
                    if (!result_is_interface) {
                        Printv(f_go_wrappers, goType(n, result), NULL);
                    } else {
                        Printv(f_go_wrappers, result_wrapper, NULL);
                    }
                    Printv(f_go_wrappers, "\n", NULL);
                    goout = goTypemapLookup("godirectorout", n, "swig_r");
                }

                String *call = NewString("");
                Printv(call, "\t", NULL);

                if (SwigType_type(result) != T_VOID) {
                    Printv(call, "swig_r = ", NULL);
                    if (result_is_interface) {
                        Printv(call, result_wrapper, "(getSwigcptr(", NULL);
                    }
                }
                Printv(call, "swig_p.", go_with_over_name, "(", NULL);

                String *goincode = NewString("");

                p = parms;
                for (int i = 0; i < parm_count; ++i) {
                    p = getParm(p);
                    if (i > 0) {
                        Printv(call, ", ", NULL);
                    }
                    SwigType *pt = Getattr(p, "type");

                    String *ln = NewString("");

                    // If the Go representation is an interface type class, then
                    // we are receiving a uintptr, and must convert to the
                    // interface.
                    bool is_interface = goTypeIsInterface(p, pt);
                    if (is_interface) {
                        // Passing is_result as true to goWrapperType gives us the
                        // name of the Go type we need to convert to an interface.
                        String *wt = goWrapperType(p, pt, true);
                        Printv(ln, wt, "(", NULL);
                        Delete(wt);
                    }

                    Printv(ln, Getattr(p, "lname"), NULL);

                    if (is_interface) {
                        Printv(ln, ")", NULL);
                    }

                    String *goin = goGetattr(p, "tmap:godirectorin");
                    if (goin == NULL) {
                        Printv(call, ln, NULL);
                    } else {
                        String *ivar = NewString("");
                        Printf(ivar, "_swig_i_%d", i);
                        String *itm = goType(p, pt);
                        Printv(f_go_wrappers, "\tvar ", ivar, " ", itm, "\n", NULL);
                        goin = Copy(goin);
                        Replaceall(goin, "$input", ln);
                        Replaceall(goin, "$result", ivar);
                        Printv(goincode, goin, "\n", NULL);
                        Delete(goin);
                        Printv(call, ivar, NULL);
                        Delete(ivar);
                    }

                    Delete(ln);

                    p = nextParm(p);
                }

                Printv(call, ")", NULL);

                if (result_is_interface) {
                    Printv(call, "))", NULL);
                }
                Printv(call, "\n", NULL);

                Printv(f_go_wrappers, "\tswig_p := swigDirectorLookup(swig_c).(*", director_struct_name, ")\n", NULL);
                Printv(f_go_wrappers, goincode, NULL);
                Printv(f_go_wrappers, call, NULL);
                Delete(call);

                if (SwigType_type(result) != T_VOID) {
                    if (goout == NULL) {
                        Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
                    } else {
                        String *tm = goImType(n, result);
                        Printv(f_go_wrappers, "\tvar swig_r_1 ", tm, "\n", NULL);
                        Replaceall(goout, "$input", "swig_r");
                        Replaceall(goout, "$result", "swig_r_1");
                        Printv(f_go_wrappers, goout, "\n", NULL);
                        Printv(f_go_wrappers, "\treturn swig_r_1\n", NULL);
                    }
                }
            }

            Printv(f_go_wrappers, "}\n\n", NULL);

            Delete(result_wrapper);

            Delete(upcall_wname);
            Delete(upcall_gc_name);
            Delete(go_with_over_name);
        }

        if (!is_ignored || is_pure_virtual) {
            // Declare the method for the director class.

            SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
            String *decl = Swig_method_decl(rtype, Getattr(n, "decl"), Getattr(n, "name"), parms, 0);
            Printv(f_c_directors_h, "  virtual ", decl, NULL);
            Delete(decl);

            String *qname = NewString("");
            Printv(qname, "SwigDirector_", class_name, "::", Getattr(n, "name"), NULL);
            decl = Swig_method_decl(rtype, Getattr(n, "decl"), qname, parms, 0);
            Printv(w->def, decl, NULL);
            Delete(decl);
            Delete(qname);

            String *throws = buildThrow(n);
            if (throws) {
                Printv(f_c_directors_h, " ", throws, NULL);
                Printv(w->def, " ", throws, NULL);
                Delete(throws);
            }

            Printv(f_c_directors_h, ";\n", NULL);

            Printv(w->def, " {\n", NULL);

            if (SwigType_type(result) != T_VOID) {
                if (!SwigType_isclass(result)) {
                    if (!(SwigType_ispointer(result) || SwigType_isreference(result))) {
                        String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(result, 0));
                        Wrapper_add_localv(w, "c_result", SwigType_lstr(result, "c_result"), construct_result, NIL);
                        Delete(construct_result);
                    } else {
                        Wrapper_add_localv(w, "c_result", SwigType_lstr(result, "c_result"), "= 0", NIL);
                    }
                } else {
                    String *cres = SwigType_lstr(result, "c_result");
                    Printf(w->code, "%s;\n", cres);
                    Delete(cres);
                }
            }

            if (!is_ignored) {
                makeDirectorMethodWrapper(n, w, callback_name);
            } else {
                assert(is_pure_virtual);
                Printv(w->code, "  _swig_gopanic(\"call to pure virtual function ", Getattr(parent, "sym:name"), name,
                       "\");\n", NULL);
                if (SwigType_type(result) != T_VOID) {
                    String *retstr = SwigType_rcaststr(result, "c_result");
                    Printv(w->code, "  return ", retstr, ";\n", NULL);
                    Delete(retstr);
                }
            }

            Printv(w->code, "}", NULL);

            Replaceall(w->code, "$symname", symname);
            Wrapper_print(w, f_c_directors);
        }

        Delete(cn);
        Delete(go_type_name);
        Delete(director_struct_name);
        Delete(interface_name);
        Delete(callback_name);
        Delete(upcall_name);
        Delete(go_name);
        DelWrapper(w);

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * makeDirectorMethodWrapper
   *
   * Emit the function wrapper for a director method.
   * ------------------------------------------------------------ */
    void makeDirectorMethodWrapper(Node *n, Wrapper *w, String *callback_name) {
        ParmList *parms = Getattr(n, "wrap:parms");
        SwigType *result = Getattr(n, "type");

        Printv(f_c_directors, "extern \"C\" ", NULL);

        String *fnname = Copy(callback_name);
        Append(fnname, "(int");

        Parm *p = parms;
        while (p) {
            while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
                p = Getattr(p, "tmap:directorin:next");
            }
            String *cg = gcCTypeForGoValue(p, Getattr(p, "type"), Getattr(p, "lname"));
            Printv(fnname, ", ", cg, NULL);
            Delete(cg);
            p = Getattr(p, "tmap:directorin:next");
        }

        Printv(fnname, ")", NULL);

        if (SwigType_type(result) == T_VOID) {
            Printv(f_c_directors, "void ", fnname, NULL);
        } else {
            String *tm = gcCTypeForGoValue(n, result, fnname);
            Printv(f_c_directors, tm, NULL);
            Delete(tm);
        }

        Delete(fnname);

        Printv(f_c_directors, ";\n", NULL);

        if (SwigType_type(result) != T_VOID) {
            String *r = NewString(Swig_cresult_name());
            String *tm = gcCTypeForGoValue(n, result, r);
            Wrapper_add_local(w, r, tm);
            Delete(tm);
            Delete(r);
        }

        String *args = NewString("");

        p = parms;
        while (p) {
            while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
                p = Getattr(p, "tmap:directorin:next");
            }

            String *pn = NewString("swig_");
            Append(pn, Getattr(p, "lname"));
            Setattr(p, "emit:directorinput", pn);

            String *tm = gcCTypeForGoValue(p, Getattr(p, "type"), pn);
            Wrapper_add_local(w, pn, tm);
            Delete(tm);

            tm = Getattr(p, "tmap:directorin");
            if (!tm) {
                Swig_warning(WARN_TYPEMAP_DIRECTORIN_UNDEF, input_file,
                             line_number, "Unable to use type %s as director method argument\n",
                             SwigType_str(Getattr(p, "type"), 0));
            } else {
                tm = Copy(tm);
                Replaceall(tm, "$input", pn);
                Replaceall(tm, "$owner", 0);
                Printv(w->code, "  ", tm, "\n", NULL);
                Delete(tm);

                Printv(args, ", ", pn, NULL);
            }

            p = Getattr(p, "tmap:directorin:next");
        }

        Printv(w->code, "  ", NULL);
        if (SwigType_type(result) != T_VOID) {
            Printv(w->code, Swig_cresult_name(), " = ", NULL);
        }
        Printv(w->code, callback_name, "(go_val", args, ");\n", NULL);

        /* Marshal outputs */
        for (p = parms; p;) {
            String *tm;
            if ((tm = Getattr(p, "tmap:directorargout"))) {
                tm = Copy(tm);
                Replaceall(tm, "$result", "jresult");
                Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
                Printv(w->code, tm, "\n", NULL);
                Delete(tm);
                p = Getattr(p, "tmap:directorargout:next");
            } else {
                p = nextSibling(p);
            }
        }

        if (SwigType_type(result) != T_VOID) {
            String *result_str = NewString("c_result");
            String *tm = Swig_typemap_lookup("directorout", n, result_str, NULL);
            if (!tm) {
                Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF, input_file, line_number,
                             "Unable to use type %s as director method result\n", SwigType_str(result, 0));
            } else {
                tm = Copy(tm);
                Replaceall(tm, "$input", Swig_cresult_name());
                Replaceall(tm, "$result", "c_result");
                Printv(w->code, "  ", tm, "\n", NULL);
                String *retstr = SwigType_rcaststr(result, "c_result");
                Printv(w->code, "  return ", retstr, ";\n", NULL);
                Delete(retstr);
                Delete(tm);
            }
            Delete(result_str);
        }
    }


    /* ------------------------------------------------------------
   * classDirectorEnd
   *
   * Complete support for a director class.
   * ------------------------------------------------------------ */

    int classDirectorEnd(Node *n) {
        (void) n;

        Printv(f_c_directors_h, " private:\n", NULL);
        Printv(f_c_directors_h, "  intgo go_val;\n", NULL);
        Printv(f_c_directors_h, "  Swig_memory *swig_mem;\n", NULL);
        Printv(f_c_directors_h, "};\n\n", NULL);

        class_name = NULL;
        class_node = NULL;

        Delete(class_receiver);
        class_receiver = NULL;

        Delete(class_methods);
        class_methods = NULL;

        return SWIG_OK;
    }

    /* ------------------------------------------------------------
   * classDirectorDisown
   *
   * I think Go does not require a disown method.
   * ------------------------------------------------------------ */

    int classDirectorDisown(Node *n) {
        (void) n;
        return SWIG_OK;
    }

    /*----------------------------------------------------------------------
   * buildThrow()
   *
   * Build and return a throw clause if needed.
   *--------------------------------------------------------------------*/

    String *buildThrow(Node *n) {
        if (Getattr(n, "noexcept"))
            return NewString("noexcept");
        ParmList *throw_parm_list = Getattr(n, "throws");
        if (!throw_parm_list && !Getattr(n, "throw"))
            return NULL;
        String *ret = NewString("throw(");
        if (throw_parm_list) {
            Swig_typemap_attach_parms("throws", throw_parm_list, NULL);
        }
        bool first = true;
        for (Parm *p = throw_parm_list; p; p = nextSibling(p)) {
            if (Getattr(p, "tmap:throws")) {
                if (first) {
                    first = false;
                } else {
                    Printv(ret, ", ", NULL);
                }
                String *s = SwigType_str(Getattr(p, "type"), 0);
                Printv(ret, s, NULL);
                Delete(s);
            }
        }
        Printv(ret, ")", NULL);
        return ret;
    }

    /*----------------------------------------------------------------------
   * extraDirectorProtectedCPPMethodsRequired()
   *
   * We don't need to check upcall when calling methods.
   *--------------------------------------------------------------------*/

    bool extraDirectorProtectedCPPMethodsRequired() const {
        return false;
    }

    /*----------------------------------------------------------------------
   * makeDispatchFunction
   *
   * Make a dispatch function for an overloaded C++ function.  The
   * receiver parameter is the receiver for a method, unless is_upcall
   * is true.  If is_upcall is true, then the receiver parameter is
   * the type of the first argument to the function.
   *--------------------------------------------------------------------*/

    int makeDispatchFunction(Node *n, String *go_name, String *receiver, bool is_static, SwigType *director_struct,
                             bool is_upcall) {
        bool is_director = director_struct ? true : false;

        String *nodetype = Getattr(n, "nodeType");
        bool is_constructor = Cmp(nodetype, "constructor") == 0;
        bool is_destructor = Cmp(nodetype, "destructor") == 0;

        bool can_use_receiver = (!is_constructor && !is_destructor && !is_upcall);

        bool use_receiver = (!is_static && can_use_receiver);

        bool add_to_interface = (interfaces && !is_constructor && !is_destructor && !is_static && !is_upcall);

        List *dispatch = Swig_overload_rank(n, false);
        int nfunc = Len(dispatch);

        SwigType *all_result;
        bool mismatch;
        if (is_constructor) {
            assert(!is_upcall);
            if (!is_director) {
                all_result = Copy(Getattr(class_node, "classtypeobj"));
            } else {
                all_result = Copy(director_struct);
            }
            mismatch = false;
        } else {
            all_result = NULL;
            mismatch = false;
            bool any_void = false;
            for (int i = 0; i < nfunc; ++i) {
                Node *nn = Getitem(dispatch, i);
                Node *ni = Getattr(nn, "directorNode") ? Getattr(nn, "directorNode") : nn;
                SwigType *result = Getattr(ni, "go:type");
                assert(result);

                if (SwigType_type(result) == T_VOID) {
                    if (all_result) {
                        mismatch = true;
                    }
                    any_void = true;
                } else {
                    if (any_void) {
                        mismatch = true;
                    } else if (!all_result) {
                        all_result = Copy(result);
                    } else if (Cmp(result, all_result) != 0) {
                        mismatch = true;
                    }
                }
            }
            if (mismatch) {
                Delete(all_result);
                all_result = NULL;
            } else if (all_result) { ;
            } else {
                all_result = NewString("void");
            }
        }

        Printv(f_go_wrappers, "func ", NULL);

        if (receiver && use_receiver) {
            Printv(f_go_wrappers, "(p ", receiver, ") ", NULL);
        }

        Printv(f_go_wrappers, go_name, "(", NULL);
        if (is_director && is_constructor) {
            Printv(f_go_wrappers, "abi interface{}, ", NULL);
            assert(!add_to_interface);
        }
        if (is_upcall) {
            Printv(f_go_wrappers, "p *", receiver, ", ", NULL);
            assert(!add_to_interface);
        }
        Printv(f_go_wrappers, "a ...interface{})", NULL);

        if (add_to_interface) {
            Printv(interfaces, "\t", go_name, "(a ...interface{})", NULL);
        }

        if (mismatch) {
            Printv(f_go_wrappers, " interface{}", NULL);
            if (add_to_interface) {
                Printv(interfaces, " interface{}", NULL);
            }
        } else if (all_result && SwigType_type(all_result) != T_VOID) {
            if (is_director && is_constructor) {
                Printv(f_go_wrappers, " ", receiver, NULL);
                if (add_to_interface) {
                    Printv(interfaces, " ", receiver, NULL);
                }
            } else {
                String *tm = goType(n, all_result);
                Printv(f_go_wrappers, " ", tm, NULL);
                if (add_to_interface) {
                    Printv(interfaces, " ", tm, NULL);
                }
                Delete(tm);
            }
        }
        Printv(f_go_wrappers, " {\n", NULL);
        if (add_to_interface) {
            Printv(interfaces, "\n", NULL);
        }

        Printv(f_go_wrappers, "\targc := len(a)\n", NULL);

        for (int i = 0; i < nfunc; ++i) {
            int fn = 0;
            Node *nn = Getitem(dispatch, i);
            Node *ni = Getattr(nn, "directorNode") ? Getattr(nn, "directorNode") : nn;
            Parm *pi = Getattr(ni, "wrap:parms");

            // If we are using a receiver, we want to ignore a leading self
            // parameter.  Because of the way this is called, there may or
            // may not be a self parameter at this point.
            if (use_receiver && pi && Getattr(pi, "self")) {
                pi = getParm(pi);
                if (pi) {
                    pi = nextParm(pi);
                }
            }

            int num_required = emit_num_required(pi);
            int num_arguments = emit_num_arguments(pi);
            bool varargs = emit_isvarargs(pi) ? true : false;

            if (varargs) {
                Printf(f_go_wrappers, "\tif argc >= %d {\n", num_required);
            } else {
                if (num_required == num_arguments) {
                    Printf(f_go_wrappers, "\tif argc == %d {\n", num_required);
                } else {
                    Printf(f_go_wrappers, "\tif argc >= %d && argc <= %d {\n", num_required, num_arguments);
                }
            }

            // Build list of collisions with the same number of arguments.
            List *coll = NewList();
            for (int k = i + 1; k < nfunc; ++k) {
                Node *nnk = Getitem(dispatch, k);
                Node *nk = Getattr(nnk, "directorNode") ? Getattr(nnk, "directorNode") : nnk;
                Parm *pk = Getattr(nk, "wrap:parms");
                if (use_receiver && pk && Getattr(pk, "self")) {
                    pk = getParm(pk);
                    if (pk) {
                        pk = nextParm(pk);
                    }
                }
                int nrk = emit_num_required(pk);
                int nak = emit_num_arguments(pk);
                if ((nrk >= num_required && nrk <= num_arguments)
                    || (nak >= num_required && nak <= num_arguments)
                    || (nrk <= num_required && nak >= num_arguments)
                    || (varargs && nrk >= num_required)) {
                    Append(coll, nk);
                }
            }

            int num_braces = 0;
            if (Len(coll) > 0 && num_arguments > 0) {
                int j = 0;
                Parm *pj = pi;
                while (pj) {
                    pj = getParm(pj);
                    if (!pj) {
                        break;
                    }

                    // If all the overloads have the same type in this position,
                    // we can omit the check.
                    SwigType *tm = goOverloadType(pj, Getattr(pj, "type"));
                    bool emitcheck = false;
                    for (int k = 0; k < Len(coll) && !emitcheck; ++k) {
                        Node *nk = Getitem(coll, k);
                        Parm *pk = Getattr(nk, "wrap:parms");
                        if (use_receiver && pk && Getattr(pk, "self")) {
                            pk = getParm(pk);
                            if (pk) {
                                pk = nextParm(pk);
                            }
                        }
                        int nak = emit_num_arguments(pk);
                        if (nak <= j)
                            continue;
                        int l = 0;
                        Parm *pl = pk;
                        while (pl && l <= j) {
                            pl = getParm(pl);
                            if (!pl) {
                                break;
                            }
                            if (l == j) {
                                SwigType *tml = goOverloadType(pl, Getattr(pl, "type"));
                                if (Cmp(tm, tml) != 0) {
                                    emitcheck = true;
                                }
                                Delete(tml);
                            }
                            pl = nextParm(pl);
                            ++l;
                        }
                    }

                    if (emitcheck) {
                        if (j >= num_required) {
                            Printf(f_go_wrappers, "\t\tif argc > %d {\n", j);
                            ++num_braces;
                        }

                        fn = i + 1;
                        Printf(f_go_wrappers, "\t\tif _, ok := a[%d].(%s); !ok {\n", j, tm);
                        Printf(f_go_wrappers, "\t\t\tgoto check_%d\n", fn);
                        Printv(f_go_wrappers, "\t\t}\n", NULL);
                    }

                    Delete(tm);

                    pj = nextParm(pj);

                    ++j;
                }
            }

            for (; num_braces > 0; --num_braces) {
                Printv(f_go_wrappers, "\t\t}\n", NULL);
            }

            // We may need to generate multiple calls if there are variable
            // argument lists involved.  Build the start of the call.

            String *start = NewString("");

            SwigType *result = Getattr(ni, "go:type");

            if (is_constructor) {
                result = all_result;
            } else if (is_destructor) {
                result = NULL;
            }

            if (result && SwigType_type(result) != T_VOID && (!all_result || SwigType_type(all_result) != T_VOID)) {
                Printv(start, "return ", NULL);
            }

            bool advance_parm = false;

            if (receiver && use_receiver) {
                Printv(start, "p.", go_name, NULL);
            } else if (can_use_receiver && !isStatic(ni) && pi && Getattr(pi, "self")) {
                // This is an overload of a static function and a non-static
                // function.
                assert(num_required > 0);
                SwigType *tm = goWrapperType(pi, Getattr(pi, "type"), true);
                String *nm = buildGoName(Getattr(ni, "sym:name"), false, isFriend(ni));
                Printv(start, "a[0].(", tm, ").", nm, NULL);
                Delete(nm);
                Delete(tm);
                advance_parm = true;
            } else {
                Printv(start, go_name, NULL);
            }

            Printv(start, Getattr(ni, "sym:overname"), "(", NULL);

            bool need_comma = false;

            if (is_director && is_constructor) {
                Printv(start, "abi", NULL);
                need_comma = true;
            }
            if (is_upcall) {
                Printv(start, "p", NULL);
                need_comma = true;
            }
            Parm *p = pi;
            int pn = 0;
            if (advance_parm) {
                p = getParm(p);
                if (p) {
                    p = nextParm(p);
                }
                ++pn;
            }
            while (pn < num_required) {
                p = getParm(p);

                if (need_comma) {
                    Printv(start, ", ", NULL);
                }

                SwigType *tm = goType(p, Getattr(p, "type"));
                Printf(start, "a[%d].(%s)", pn, tm);
                Delete(tm);

                need_comma = true;
                ++pn;
                p = nextParm(p);
            }

            String *end = NULL;
            if (!result || SwigType_type(result) == T_VOID || (all_result && SwigType_type(all_result) == T_VOID)) {
                end = NewString("");
                Printv(end, "return", NULL);
                if (!all_result || SwigType_type(all_result) != T_VOID) {
                    Printv(end, " 0", NULL);
                }
            }

            if (num_required == num_arguments) {
                Printv(f_go_wrappers, "\t\t", start, ")\n", NULL);
                if (end) {
                    Printv(f_go_wrappers, "\t\t", end, "\n", NULL);
                }
            } else {
                Printv(f_go_wrappers, "\t\tswitch argc {\n", NULL);
                for (int j = num_required; j <= num_arguments; ++j) {
                    Printf(f_go_wrappers, "\t\tcase %d:\n", j);
                    Printv(f_go_wrappers, "\t\t\t", start, NULL);
                    bool nc = need_comma;
                    for (int k = num_required; k < j; ++k) {
                        if (nc) {
                            Printv(f_go_wrappers, ", ", NULL);
                        }
                        Printf(f_go_wrappers, "a[%d]", k);
                        nc = true;
                    }
                    Printv(f_go_wrappers, ")\n", NULL);
                    if (end) {
                        Printv(f_go_wrappers, "\t\t\t", end, "\n", NULL);
                    }
                }
                Printv(f_go_wrappers, "\t\t}\n", NULL);
            }

            Printv(f_go_wrappers, "\t}\n", NULL);

            if (fn != 0) {
                Printf(f_go_wrappers, "check_%d:\n", fn);
            }

            Delete(coll);
        }

        Printv(f_go_wrappers, "\tpanic(\"No match for overloaded function call\")\n", NULL);
        Printv(f_go_wrappers, "}\n\n", NULL);

        Delete(all_result);
        Delete(dispatch);

        return SWIG_OK;
    }

    /* ----------------------------------------------------------------------
   * checkFunctionVisibility()
   *
   * Return true if we should write out a function based on its
   * visibility, false otherwise.
   * ---------------------------------------------------------------------- */

    bool checkFunctionVisibility(Node *n, Node *parent) {
        // Write out a public function.
        if (is_public(n))
            return true;
        // Don't write out a private function.
        if (is_private(n))
            return false;
        // Write a protected function for a director class in
        // dirprot_mode.
        if (parent == NULL) {
            return false;
        }
        if (dirprot_mode() && Swig_directorclass(parent))
            return true;
        // Otherwise don't write out a protected function.
        return false;
    }


    /* ----------------------------------------------------------------------
   * exportedName()
   *
   * Given a C/C++ name, return a name in Go which will be exported.
   * If the first character is an upper case letter, this returns a
   * copy of its argument.  If the first character is a lower case
   * letter, this forces it to upper case.  Otherwise, this prepends
   * 'X'.
   * ---------------------------------------------------------------------- */

    String *exportedName(SwigType *name) {
        SwigType *copy = Copy(name);
        char c = *Char(copy);
        if (islower(c)) {
            char l[2];
            char u[2];
            l[0] = c;
            l[1] = '\0';
            u[0] = toupper(c);
            u[1] = '\0';
            Replace(copy, l, u, DOH_REPLACE_FIRST);
        } else if (!isalpha(c)) {
            char l[2];
            char u[3];
            l[0] = c;
            l[1] = '\0';
            u[0] = 'X';
            u[1] = c;
            u[2] = '\0';
            Replace(copy, l, u, DOH_REPLACE_FIRST);
        }
        String *ret = Swig_name_mangle_type(copy);
        Delete(copy);
        return ret;
    }

    /* ----------------------------------------------------------------------
   * removeClassname()
   *
   * If the name starts with the current class name, followed by an
   * underscore, remove it.  If there is no current class name, this
   * simply returns a copy of the name.  This undoes Swig's way of
   * recording the class name in a member name.
   * ---------------------------------------------------------------------- */

    String *removeClassname(String *name) {
        String *copy = Copy(name);
        if (class_name) {
            char *p = Char(name);
            if (Strncmp(name, class_name, Len(class_name)) == 0 && p[Len(class_name)] == '_') {
                Replace(copy, class_name, "", DOH_REPLACE_FIRST);
                Replace(copy, "_", "", DOH_REPLACE_FIRST);
            }
        }
        return copy;
    }

    /* ----------------------------------------------------------------------
   * buildGoName()
   *
   * Build the name to use for an ordinary function, variable, or
   * whatever in Go.  The name argument is something like the sym:name
   * attribute of the node.  If is_static is false, this could be a
   * method, and the returned name will be the name of the
   * method--i.e., it will not include the class name.
   * ---------------------------------------------------------------------- */

    String *buildGoName(String *name, bool is_static, bool is_friend) {
        String *nw = NewString("");
        if (is_static && !is_friend && class_name) {
            String *c1 = exportedName(class_name);
            Append(nw, c1);
            Delete(c1);
        }
        String *c2 = removeClassname(name);
        String *c3 = exportedName(c2);
        Append(nw, c3);
        Delete(c2);
        Delete(c3);
        String *ret = Swig_name_mangle_string(nw);
        Delete(nw);
        return ret;
    }

    /* ----------------------------------------------------------------------
   * buildGoWrapperName()
   *
   * Build the name to use for a Go wrapper function.  This is a
   * function called by the real Go function in order to convert C++
   * classes from interfaces to pointers, and other such conversions
   * between the Go type and the C++ type.
   * ---------------------------------------------------------------------- */

    String *buildGoWrapperName(String *name, String *overname) {
        String *s1 = NewString("_swig_wrap_");
        Append(s1, name);
        String *s2 = Swig_name_mangle_string(s1);
        Delete(s1);
        if (overname) {
            Append(s2, overname);
        }
        return s2;
    }

    /* ----------------------------------------------------------------------
   * checkNameConflict()
   *
   * Check for a name conflict on the name we are going to use in Go.
   * These conflicts are likely because of the enforced
   * capitalization.  When we find one, issue a warning and return
   * false.  If the name is OK, return true.
   * ---------------------------------------------------------------------- */

    bool checkNameConflict(String *name, Node *n, const_String_or_char_ptr scope) {
        Node *lk = symbolLookup(name, scope);
        if (lk) {
            String *n1 = Getattr(n, "sym:name");
            if (!n1) {
                n1 = Getattr(n, "name");
            }
            String *n2 = Getattr(lk, "sym:name");
            if (!n2) {
                n2 = Getattr(lk, "name");
            }
            Swig_warning(WARN_GO_NAME_CONFLICT, input_file, line_number,
                         "Ignoring '%s' due to Go name ('%s') conflict with '%s'\n",
                         n1, name, n2);
            return false;
        }
        bool r = addSymbol(name, n, scope) ? true : false;
        assert(r);
        (void) r;
        return true;
    }

    /* ----------------------------------------------------------------------
   * checkIgnoredParameters()
   *
   * If any of the parameters of this function, or the return type,
   * are ignored due to a name conflict, give a warning and return
   * false.
   * ---------------------------------------------------------------------- */

    bool checkIgnoredParameters(Node *n, String *go_name) {
        ParmList *parms = Getattr(n, "parms");
        if (parms) {
            Wrapper *dummy = NewWrapper();
            emit_attach_parmmaps(parms, dummy);
            int parm_count = emit_num_arguments(parms);
            Parm *p = parms;

            for (int i = 0; i < parm_count; ++i) {
                p = getParm(p);
                if (!checkIgnoredType(n, go_name, Getattr(p, "type"))) {
                    DelWrapper(dummy);
                    return false;
                }
                p = nextParm(p);
            }

            DelWrapper(dummy);
        }

        if (!checkIgnoredType(n, go_name, Getattr(n, "type"))) {
            return false;
        }

        return true;
    }

    /* ----------------------------------------------------------------------
   * checkIgnoredType()
   *
   * If this type is being ignored due to a name conflict, give a
   * warning and return false.
   * ---------------------------------------------------------------------- */

    bool checkIgnoredType(Node *n, String *go_name, SwigType *type) {
        if (hasGoTypemap(n, type)) {
            return true;
        }

        SwigType *t = SwigType_typedef_resolve_all(type);

        bool ret = true;
        bool is_conflict = false;
        Node *e = Language::enumLookup(t);
        if (e) {
            if (GetFlag(e, "go:conflict")) {
                is_conflict = true;
            }
        } else if (SwigType_issimple(t)) {
            Node *cn = classLookup(t);
            if (cn) {
                if (GetFlag(cn, "go:conflict")) {
                    is_conflict = true;
                }
            }
        } else if (SwigType_ispointer(t) || SwigType_isarray(t) || SwigType_isqualifier(t) || SwigType_isreference(t)) {
            SwigType *r = Copy(t);
            if (SwigType_ispointer(r)) {
                SwigType_del_pointer(r);
            } else if (SwigType_isarray(r)) {
                SwigType_del_array(r);
            } else if (SwigType_isqualifier(r)) {
                SwigType_del_qualifier(r);
            } else {
                SwigType_del_reference(r);
            }

            if (!checkIgnoredType(n, go_name, r)) {
                ret = false;
            }

            Delete(r);
        }

        if (is_conflict) {
            String *s = SwigType_str(t, NULL);
            Swig_warning(WARN_GO_NAME_CONFLICT, input_file, line_number,
                         "Ignoring '%s' (Go name '%s') due to Go name conflict for parameter or result type '%s'\n",
                         Getattr(n, "name"), go_name, s);
            Delete(s);
            ret = false;
        }

        Delete(t);

        return ret;
    }

    /* ----------------------------------------------------------------------
   * goType()
   *
   * Given a SWIG type, return a string for the type in Go.
   * ---------------------------------------------------------------------- */

    String *goType(Node *n, SwigType *type) {
        return goTypeWithInfo(n, type, false, NULL);
    }

    /* ----------------------------------------------------------------------
   * goImType()
   *
   * Given a SWIG type, return a string for the intermediate Go type
   * to pass to C/C++.  This is like goType except that it looks for
   * an imtype typemap entry first.
   * ---------------------------------------------------------------------- */

    String *goImType(Node *n, SwigType *type) {
        return goTypeWithInfo(n, type, true, NULL);
    }

    /* ----------------------------------------------------------------------
   * goTypeWithInfo()
   *
   * Like goType, but return some more information.
   *
   * If use_imtype is true, this look for a imtype typemap entry.
   *
   * If the p_is_interface parameter is not NULL, this sets
   * *p_is_interface to indicate whether this type is going to be
   * represented by a Go interface type.  These are cases where the Go
   * code needs to make some adjustments when passing values back and
   * forth with C/C++.
   * ---------------------------------------------------------------------- */

    String *goTypeWithInfo(Node *n, SwigType *type, bool use_imtype, bool *p_is_interface) {
        if (p_is_interface) {
            *p_is_interface = false;
        }

        String *ret = NULL;
        if (use_imtype) {
            if (n && Cmp(type, Getattr(n, "type")) == 0) {
                if (Strcmp(Getattr(n, "nodeType"), "parm") == 0) {
                    ret = Getattr(n, "tmap:imtype");
                }
                if (!ret) {
                    ret = Swig_typemap_lookup("imtype", n, "", NULL);
                }
            } else {
                Parm *p = NewParm(type, "goImType", n);
                ret = Swig_typemap_lookup("imtype", p, "", NULL);
                Delete(p);
            }
        }
        if (!ret) {
            if (n && Cmp(type, Getattr(n, "type")) == 0) {
                if (Strcmp(Getattr(n, "nodeType"), "parm") == 0) {
                    ret = Getattr(n, "tmap:gotype");
                }
                if (!ret) {
                    ret = Swig_typemap_lookup("gotype", n, "", NULL);
                }
            } else {
                Parm *p = NewParm(type, "goType", n);
                ret = Swig_typemap_lookup("gotype", p, "", NULL);
                Delete(p);
            }
        }

        if (ret && Strstr(ret, "$gotypename") != 0) {
            ret = NULL;
        }

        if (ret) {
            return Copy(ret);
        }

        SwigType *t = SwigType_typedef_resolve_all(type);

        if (SwigType_isenum(t)) {
            Node *e = Language::enumLookup(t);
            if (e) {
                ret = goEnumName(e);
            } else if (Strcmp(t, "enum ") == 0) {
                ret = NewString("int");
            } else {
                // An unknown enum - one that has not been parsed (neither a C enum forward reference nor a definition) or an ignored enum
                String *tt = Copy(t);
                Replace(tt, "enum ", "", DOH_REPLACE_ANY);
                ret = exportedName(tt);
                Setattr(undefined_enum_types, t, ret);
                Delete(tt);
            }
        } else if (SwigType_isfunctionpointer(t) || SwigType_isfunction(t)) {
            ret = NewString("_swig_fnptr");
        } else if (SwigType_ismemberpointer(t)) {
            ret = NewString("_swig_memberptr");
        } else if (SwigType_issimple(t)) {
            Node *cn = classLookup(t);
            if (cn) {
                ret = Getattr(cn, "sym:name");
                if (!ret) {
                    ret = Getattr(cn, "name");
                }
                ret = exportedName(ret);

                Node *cnmod = Getattr(cn, "module");
                if (!cnmod || Strcmp(Getattr(cnmod, "name"), module) == 0) {
                    Setattr(undefined_types, t, t);
                } else {
                    String *nw = NewString("");
                    Printv(nw, getModuleName(Getattr(cnmod, "name")), ".", ret, NULL);
                    Delete(ret);
                    ret = nw;
                }
            } else {
                // SWIG does not know about this type.
                ret = exportedName(t);
                Setattr(undefined_types, t, t);
            }
            if (p_is_interface) {
                *p_is_interface = true;
            }
        } else if (SwigType_ispointer(t) || SwigType_isarray(t)) {
            SwigType *r = Copy(t);
            if (SwigType_ispointer(r)) {
                SwigType_del_pointer(r);
            } else {
                SwigType_del_array(r);
            }

            if (SwigType_type(r) == T_VOID) {
                ret = NewString("uintptr");
            } else {
                bool is_interface;
                String *base = goTypeWithInfo(n, r, false, &is_interface);

                // At the Go level, an unknown or class type is handled as an
                // interface wrapping a pointer.  This means that if a
                // function returns the C type X, we will be wrapping the C
                // type X*.  In Go we will call that type X.  That means that
                // if a C function expects X*, we can pass the Go type X.  And
                // that means that when we see the C type X*, we should use
                // the Go type X.

                // The is_interface variable tells us this.  However, it will
                // be true both for the case of X and for the case of X*.  If
                // r is a pointer here, then we are looking at X**.  There is
                // really no good way for us to handle that.
                bool is_pointer_to_pointer = false;
                if (is_interface) {
                    SwigType *c = Copy(r);
                    if (SwigType_isqualifier(c)) {
                        SwigType_del_qualifier(c);
                        if (SwigType_ispointer(c) || SwigType_isarray(c)) {
                            is_pointer_to_pointer = true;
                        }
                    }
                    Delete(c);
                }

                if (is_interface) {
                    if (!is_pointer_to_pointer) {
                        ret = base;
                        if (p_is_interface) {
                            *p_is_interface = true;
                        }
                    } else {
                        ret = NewString("uintptr");
                    }
                } else {
                    ret = NewString("*");
                    Append(ret, base);
                    Delete(base);
                }
            }

            Delete(r);
        } else if (SwigType_isreference(t)) {
            SwigType *r = Copy(t);
            SwigType_del_reference(r);

            // If this is a const reference, and we are looking at a pointer
            // to it, then we just use the pointer we already have.
            bool add_pointer = true;
            if (SwigType_isqualifier(r)) {
                String *q = SwigType_parm(r);
                if (Strcmp(q, "const") == 0) {
                    SwigType *c = Copy(r);
                    SwigType_del_qualifier(c);
                    if (SwigType_ispointer(c)) {
                        add_pointer = false;
                    }
                    Delete(c);
                }
            }
            if (add_pointer) {
                SwigType_add_pointer(r);
            }
            ret = goTypeWithInfo(n, r, false, p_is_interface);
            Delete(r);
        } else if (SwigType_isqualifier(t)) {
            SwigType *r = Copy(t);
            SwigType_del_qualifier(r);
            ret = goTypeWithInfo(n, r, false, p_is_interface);
            Delete(r);
        } else if (SwigType_isvarargs(t)) {
            ret = NewString("[]interface{}");
        }

        Delete(t);

        if (!ret) {
            Swig_warning(WARN_LANG_NATIVE_UNIMPL, input_file, line_number, "No Go typemap defined for %s\n",
                         SwigType_str(type, 0));
            ret = NewString("uintptr");
        }

        return ret;
    }

    /* ----------------------------------------------------------------------
   * cgoTypeForGoValue()
   *
   * Given a SWIG type, return a string for the C type to use for the
   * cgo wrapper code.  This always returns a simple identifier, since
   * it is used in Go code as C.name.
   *
   * This sets *c_struct_type if the C type uses a struct where the Go
   * type uses a simple type.  This is true for strings and slices.
   * When this is true the Go code has to jump through unsafe hoops to
   * pass the type checker.
   * ---------------------------------------------------------------------- */

    String *cgoTypeForGoValue(Node *n, SwigType *type, bool *c_struct_type) {
        *c_struct_type = false;

        bool is_interface;
        String *go_type = goTypeWithInfo(n, type, true, &is_interface);
        if (is_interface) {
            Delete(go_type);
            return NewString("uintptr_t");
        }
        if (Strcmp(go_type, "uintptr") == 0) {
            Delete(go_type);
            return NewString("uintptr_t");
        }
        if (((char *) Char(go_type))[0] == '*') {
            // Treat all pointers as void*.  There is no meaningful type
            // checking going on here anyhow, and that lets us avoid
            // worrying about defining the base type of the pointer.
            Delete(go_type);
            return NewString("swig_voidp");
        }

        // Check for some Go types that are really pointers under the covers.
        bool is_hidden_pointer = Strncmp(go_type, "func(", 5) == 0 || Strncmp(go_type, "map[", 4) == 0 ||
                                 Strncmp(go_type, "chan ", 5) == 0;

        Delete(go_type);

        String *ct = Getattr(n, "emit:cgotype");
        if (ct) {
            *c_struct_type = Getattr(n, "emit:cgotypestruct") ? true : false;
            return Copy(ct);
        }

        String *t = Copy(type);
        if (SwigType_isarray(t) && Getattr(n, "tmap:gotype") == NULL) {
            SwigType_del_array(t);
            SwigType_add_pointer(t);
        }

        bool add_typedef = true;

        static int count;
        ++count;
        ct = NewStringf("swig_type_%d", count);

        String *gct = gcCTypeForGoValue(n, t, ct);
        Delete(t);

        if (Strncmp(gct, "_gostring_", 10) == 0 || Strncmp(gct, "_goslice_", 9) == 0 ||
            Strncmp(gct, "_goarray_", 9) == 0) {
            *c_struct_type = true;
            Setattr(n, "emit:cgotypestruct", type);
        } else {
            char *p = Strstr(gct, ct);
            if (p != NULL && p > (char *) Char(gct) && p[-1] == '*' && p[Len(ct)] == '\0') {
                // Treat all pointers as void*.  See above.
                Delete(ct);
                --count;
                ct = NewString("swig_voidp");
                add_typedef = false;
                if (is_hidden_pointer) {
                    // A Go type that is really a pointer, like func, map, chan,
                    // is being represented in C by a pointer.  This is fine,
                    // but we have to memcpy the type rather than simply
                    // converting it.
                    *c_struct_type = true;
                    Setattr(n, "emit:cgotypestruct", type);
                }
            }

            if (Strncmp(gct, "bool ", 5) == 0) {
                // Change the C++ type bool to the C type _Bool.
                Replace(gct, "bool", "_Bool", DOH_REPLACE_FIRST);
            }
            if (Strncmp(gct, "intgo ", 6) == 0) {
                // We #define intgo to swig_intgo for the cgo comment.
                Replace(gct, "intgo", "swig_intgo", DOH_REPLACE_FIRST);
            }
            p = Strstr(gct, ct);
            if (p != NULL && p > (char *) Char(gct) && p[-1] == ' ' && p[Len(ct)] == '\0') {
                String *q = NewStringWithSize(gct, Len(gct) - Len(ct) - 1);
                if (validIdentifier(q)) {
                    // This is a simple type name, and we can use it directly.
                    Delete(ct);
                    --count;
                    ct = q;
                    add_typedef = false;
                }
            }
        }
        if (add_typedef) {
            Printv(f_cgo_comment_typedefs, "typedef ", gct, ";\n", NULL);
        }

        Setattr(n, "emit:cgotype", ct);

        Delete(gct);

        return Copy(ct);
    }

    /* ----------------------------------------------------------------------
   * goWrapperType()
   *
   * Given a type, return a string for the type to use for the wrapped
   * Go function.  This function exists because for a C++ class we
   * need to convert interface and reference types.
   * ---------------------------------------------------------------------- */

    String *goWrapperType(Node *n, SwigType *type, bool is_result) {
        bool is_interface;
        String *ret = goTypeWithInfo(n, type, true, &is_interface);

        // If this is an interface, we want to pass the real type.
        if (is_interface) {
            Delete(ret);
            if (!is_result) {
                ret = NewString("uintptr");
            } else {
                SwigType *ty = SwigType_typedef_resolve_all(type);
                while (true) {
                    if (SwigType_ispointer(ty)) {
                        SwigType_del_pointer(ty);
                    } else if (SwigType_isarray(ty)) {
                        SwigType_del_array(ty);
                    } else if (SwigType_isreference(ty)) {
                        SwigType_del_reference(ty);
                    } else if (SwigType_isqualifier(ty)) {
                        SwigType_del_qualifier(ty);
                    } else {
                        break;
                    }
                }
                assert(SwigType_issimple(ty));
                String *p = goCPointerType(ty, true);
                Delete(ty);
                ret = p;
            }
        }

        return ret;
    }

    /* ----------------------------------------------------------------------
   * goOverloadType()
   *
   * Given a type, return the Go type to use when dispatching of
   * overloaded functions.  This is normally just the usual Go type.
   * However, for a C++ class, the usual Go type is an interface type.
   * And if that interface type represents a C++ type that SWIG does
   * not know about, then the interface type generated for any C++
   * class will match that interface.  So for that case, we match on
   * the underlying integer type.
   *
   * It has to work this way so that we can handle a derived type of a
   * %ignore'd type.  It's unlikely that anybody will have a value of
   * an undefined type, but we support it because it worked in the
   * past.
   * ---------------------------------------------------------------------- */

    String *goOverloadType(Node *n, SwigType *type) {
        SwigType *ty = SwigType_typedef_resolve_all(type);
        while (true) {
            if (SwigType_ispointer(ty)) {
                SwigType_del_pointer(ty);
            } else if (SwigType_isarray(ty)) {
                SwigType_del_array(ty);
            } else if (SwigType_isreference(ty)) {
                SwigType_del_reference(ty);
            } else if (SwigType_isqualifier(ty)) {
                SwigType_del_qualifier(ty);
            } else {
                break;
            }
        }

        String *go_type = goType(n, ty);

        if (Getattr(undefined_types, ty) && !Getattr(defined_types, go_type)) {
            Delete(go_type);
            return goWrapperType(n, type, true);
        }

        Delete(go_type);
        return goType(n, type);
    }

    /* ----------------------------------------------------------------------
   * goCPointerType()
   *
   * Return the name of the Go type to use for the C pointer value.
   * The regular C type is the name of an interface type which wraps a
   * pointer whose name is returned by this function.
   * ---------------------------------------------------------------------- */

    String *goCPointerType(SwigType *type, bool add_to_hash) {
        SwigType *ty = SwigType_typedef_resolve_all(type);
        Node *cn = classLookup(ty);
        String *ex;
        String *ret;
        if (!cn) {
            if (add_to_hash) {
                Setattr(undefined_types, ty, ty);
            }
            ret = NewString("Swigcptr");
            ex = exportedName(ty);
            Append(ret, ex);
        } else {
            String *cname = Getattr(cn, "sym:name");
            if (!cname) {
                cname = Getattr(cn, "name");
            }
            ex = exportedName(cname);
            Node *cnmod = Getattr(cn, "module");
            if (!cnmod || Strcmp(Getattr(cnmod, "name"), module) == 0) {
                if (add_to_hash) {
                    Setattr(undefined_types, ty, ty);
                }
                ret = NewString("Swigcptr");
                Append(ret, ex);
            } else {
                ret = NewString("");
                Printv(ret, getModuleName(Getattr(cnmod, "name")), ".Swigcptr", ex, NULL);
            }
        }
        Delete(ty);
        Delete(ex);
        return ret;
    }

    /* ----------------------------------------------------------------------
   * gcCTypeForGoValue()
   *
   * Given a type, return the C/C++ type which will be used to catch
   * the value in Go.  This is the gc version.
   * ---------------------------------------------------------------------- */

    String *gcCTypeForGoValue(Node *n, SwigType *type, String *name) {
        bool is_interface;
        String *gt = goTypeWithInfo(n, type, true, &is_interface);

        String *tail = NewString("");
        SwigType *t = SwigType_typedef_resolve_all(type);
        bool is_const_ref = false;
        if (SwigType_isreference(t)) {
            SwigType *tt = Copy(t);
            SwigType_del_reference(tt);
            if (SwigType_isqualifier(tt)) {
                String *q = SwigType_parm(tt);
                if (Strcmp(q, "const") == 0) {
                    is_const_ref = true;
                }
            }
            Delete(tt);
        }
        if (!is_const_ref) {
            while (Strncmp(gt, "*", 1) == 0) {
                Replace(gt, "*", "", DOH_REPLACE_FIRST);
                Printv(tail, "*", NULL);
            }
        }
        Delete(t);

        bool is_string = Strcmp(gt, "string") == 0;
        bool is_slice = Strncmp(gt, "[]", 2) == 0;
        bool left = Strncmp(gt, "[", 1) == 0;
        bool right = Strncmp(Char(gt) + 2, "]", 1) == 0;
        bool is_array = left && right;
        bool is_function = Strcmp(gt, "_swig_fnptr") == 0;
        bool is_member = Strcmp(gt, "_swig_memberptr") == 0;
        bool is_complex64 = Strcmp(gt, "complex64") == 0;
        bool is_complex128 = Strcmp(gt, "complex128") == 0;
        bool is_bool = false;
        bool is_int8 = false;
        bool is_int16 = false;
        bool is_int = Strcmp(gt, "int") == 0 || Strcmp(gt, "uint") == 0;
        bool is_int32 = false;
        bool is_int64 = false;
        bool is_float32 = false;
        bool is_float64 = false;

        bool has_typemap = (n != NULL && Getattr(n, "tmap:gotype") != NULL) || hasGoTypemap(n, type);
        if (has_typemap) {
            is_bool = Strcmp(gt, "bool") == 0;
            is_int8 = Strcmp(gt, "int8") == 0 || Strcmp(gt, "uint8") == 0 || Strcmp(gt, "byte") == 0;
            is_int16 = Strcmp(gt, "int16") == 0 || Strcmp(gt, "uint16") == 0;
            is_int32 = Strcmp(gt, "int32") == 0 || Strcmp(gt, "uint32") == 0;
            is_int64 = Strcmp(gt, "int64") == 0 || Strcmp(gt, "uint64") == 0;
            is_float32 = Strcmp(gt, "float32") == 0;
            is_float64 = Strcmp(gt, "float64") == 0;
        }
        Delete(gt);

        String *ret;
        if (is_string) {
            // Note that we don't turn a reference to a string into a
            // pointer to a string.  Strings are immutable anyhow.
            ret = NewString("");
            Printv(ret, "_gostring_", tail, " ", name, NULL);
            Delete(tail);
            return ret;
        } else if (is_slice) {
            // Slices are always passed as a _goslice_, whether or not references
            // are involved.
            ret = NewString("");
            Printv(ret, "_goslice_", tail, " ", name, NULL);
            Delete(tail);
            return ret;
        } else if (is_array) {
            // Slices are always passed as a _goarray_, whether or not references
            // are involved.
            ret = NewString("");
            Printv(ret, "_goarray_", tail, " ", name, NULL);
            Delete(tail);
            return ret;
        } else if (is_function || is_member) {
            ret = NewString("");
            Printv(ret, "void*", tail, " ", name, NULL);
            Delete(tail);
            return ret;
        } else if (is_complex64) {
            ret = NewString("_Complex float ");
        } else if (is_complex128) {
            ret = NewString("_Complex double ");
        } else if (is_interface) {
            SwigType *t = SwigType_typedef_resolve_all(type);
            if (SwigType_ispointer(t)) {
                SwigType_del_pointer(t);
            }
            if (SwigType_isreference(t)) {
                SwigType_del_reference(t);
            }
            SwigType_add_pointer(t);
            ret = SwigType_lstr(t, name);
            Delete(t);
            Delete(tail);
            return ret;
        } else {
            SwigType *t = SwigType_typedef_resolve_all(type);
            if (!has_typemap && SwigType_isreference(t)) {
                // A const reference to a known type, or to a pointer, is not
                // mapped to a pointer.
                SwigType_del_reference(t);
                if (SwigType_isqualifier(t)) {
                    String *q = SwigType_parm(t);
                    if (Strcmp(q, "const") == 0) {
                        SwigType_del_qualifier(t);
                        if (hasGoTypemap(n, t) || SwigType_ispointer(t)) {
                            if (is_int) {
                                ret = NewString("intgo ");
                                Append(ret, name);
                            } else if (is_int64) {
                                ret = NewString("long long ");
                                Append(ret, name);
                            } else {
                                ret = SwigType_lstr(t, name);
                            }
                            Delete(q);
                            Delete(t);
                            Delete(tail);
                            return ret;
                        }
                    }
                    Delete(q);
                }
            }

            if (Language::enumLookup(t) != NULL) {
                is_int = true;
            } else {
                SwigType *tstripped = SwigType_strip_qualifiers(t);
                if (SwigType_isenum(tstripped))
                    is_int = true;
                Delete(tstripped);
            }

            Delete(t);
            if (is_bool) {
                ret = NewString("bool ");
            } else if (is_int8) {
                ret = NewString("char ");
            } else if (is_int16) {
                ret = NewString("short ");
            } else if (is_int) {
                ret = NewString("intgo ");
            } else if (is_int32) {
                ret = NewString("int ");
            } else if (is_int64) {
                ret = NewString("long long ");
            } else if (is_float32) {
                ret = NewString("float ");
            } else if (is_float64) {
                ret = NewString("double ");
            } else {
                Delete(tail);
                return SwigType_lstr(type, name);
            }
        }

        Append(ret, tail);
        if (!has_typemap && SwigType_isreference(type)) {
            Append(ret, "* ");
        }
        Append(ret, name);
        Delete(tail);
        return ret;
    }

    /* ----------------------------------------------------------------------
   * goTypeIsInterface
   *
   * Return whether this C++ type is represented as an interface type
   * in Go.  These types require adjustments in the Go code when
   * passing them back and forth between Go and C++.
   * ---------------------------------------------------------------------- */

    bool goTypeIsInterface(Node *n, SwigType *type) {
        bool is_interface;
        Delete(goTypeWithInfo(n, type, false, &is_interface));
        return is_interface;
    }

    /* ----------------------------------------------------------------------
   * hasGoTypemap
   *
   * Return whether a type has a "gotype" typemap entry.
   * ---------------------------------------------------------------------- */

    bool hasGoTypemap(Node *n, SwigType *type) {
        Parm *p = NewParm(type, "test", n);
        SwigType *tm = Swig_typemap_lookup("gotype", p, "", NULL);
        Delete(p);
        if (tm && Strstr(tm, "$gotypename") == 0) {
            Delete(tm);
            return true;
        }
        Delete(tm);
        return false;
    }

    /* ----------------------------------------------------------------------
   * goEnumName()
   *
   * Given an enum node, return a string to use for the enum type in Go.
   * ---------------------------------------------------------------------- */

    String *goEnumName(Node *n) {
        String *ret = Getattr(n, "go:enumname");
        if (ret) {
            return Copy(ret);
        }

        if (Equal(Getattr(n, "type"), "enum ")) {
            return NewString("int");
        }

        SwigType *type = Getattr(n, "enumtype");
        assert(type);
        char *p = Char(type);
        int len = Len(type);
        SwigType *s = NewString("");
        bool capitalize = true;
        for (int i = 0; i < len; ++i, ++p) {
            if (*p == ':') {
                ++i;
                ++p;
                assert(*p == ':');
                capitalize = true;
            } else if (capitalize) {
                Putc(toupper(*p), s);
                capitalize = false;
            } else {
                Putc(*p, s);
            }
        }

        ret = Swig_name_mangle_type(s);
        Delete(s);
        return ret;
    }


    /* ----------------------------------------------------------------------
   * getParm()
   *
   * Get the real parameter to use.
   * ---------------------------------------------------------------------- */

    Parm *getParm(Parm *p) {
        while (p && checkAttribute(p, "tmap:in:numinputs", "0")) {
            p = Getattr(p, "tmap:in:next");
        }
        return p;
    }

    /* ----------------------------------------------------------------------
   * nextParm()
   *
   * Return the next parameter.
   * ---------------------------------------------------------------------- */

    Parm *nextParm(Parm *p) {
        if (!p) {
            return NULL;
        } else if (Getattr(p, "tmap:in")) {
            return Getattr(p, "tmap:in:next");
        } else {
            return nextSibling(p);
        }
    }

    /* ----------------------------------------------------------------------
   * isStatic
   *
   * Return whether a node should be considered as static rather than
   * as a member.
   * ---------------------------------------------------------------------- */

    bool isStatic(Node *n) {
        String *storage = Getattr(n, "storage");
        return (storage && (Swig_storage_isstatic(n) || Strstr(storage, "friend")) &&
                (!SmartPointer || !Getattr(n, "allocate:smartpointeraccess")));
    }

    /* ----------------------------------------------------------------------
   * isFriend
   *
   * Return whether a node is a friend.
   * ---------------------------------------------------------------------- */

    bool isFriend(Node *n) {
        String *storage = Getattr(n, "storage");
        return storage && Strstr(storage, "friend");
    }

    /* ----------------------------------------------------------------------
   * goGetattr
   *
   * Fetch an attribute from a node but return NULL if it is the empty string.
   * ---------------------------------------------------------------------- */
    Node *goGetattr(Node *n, const char *name) {
        Node *ret = Getattr(n, name);
        if (ret != NULL && Len(ret) == 0) {
            ret = NULL;
        }
        return ret;
    }

    /* ----------------------------------------------------------------------
   * goTypemapLookup
   *
   * Look up a typemap but return NULL if it is the empty string.
   * ---------------------------------------------------------------------- */
    String *goTypemapLookup(const char *name, Node *node, const char *lname) {
        String *ret = Swig_typemap_lookup(name, node, lname, NULL);
        if (ret != NULL && Len(ret) == 0) {
            ret = NULL;
        }
        return ret;
    }

    /* ----------------------------------------------------------------------
   * getModuleName
   *
   * Return the name of a module. This is different from module path:
   * "some/path/to/module" -> "module".
   * ---------------------------------------------------------------------- */

    String *getModuleName(String *module_path) {
        char *suffix = strrchr(Char(module_path), '/');
        if (suffix == NULL) {
            return module_path;
        }
        return Str(suffix + 1);
    }

};                /* class GO */

/* -----------------------------------------------------------------------------
 * swig_go()    - Instantiate module
 * ----------------------------------------------------------------------------- */

static Language *new_swig_go() {
    return new GO();
}

extern "C" Language *swig_go(void) {
    return new_swig_go();
}

/* -----------------------------------------------------------------------------
 * Static member variables
 * ----------------------------------------------------------------------------- */

// Usage message.
const char *const GO::usage = "\
Go Options (available with -go)\n\
     -cgo                - Generate cgo input files\n\
     -no-cgo             - Do not generate cgo input files\n\
     -gccgo              - Generate code for gccgo rather than gc\n\
     -go-pkgpath <p>     - Like gccgo -fgo-pkgpath option\n\
     -go-prefix <p>      - Like gccgo -fgo-prefix option\n\
     -import-prefix <p>  - Prefix to add to %import directives\n\
     -intgosize <s>      - Set size of Go int type--32 or 64 bits\n\
     -package <name>     - Set name of the Go package to <name>\n\
     -use-shlib          - Force use of a shared library\n\
     -soname <name>      - Set shared library holding C/C++ code to <name>\n\
\n";
